-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp_server.cpp
134 lines (117 loc) · 4.37 KB
/
http_server.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define BACKLOG 10 // max concurrent connections
#define MAX_DATA_SIZE 100 // receive message buffer size
#define FILE_READ_SIZE 512 // send file chunk size
#define HTTP_200 "HTTP/1.1 200 OK"
#define HTTP_400 "HTTP/1.1 400 Bad Request"
#define HTTP_404 "HTTP/1.1 404 Not Found"
using namespace std;
int main(int argc, char const *argv[]) {
int pid;
// int yes = 1; // setsockopt variable
int bytes_received;
char rec_buf[MAX_DATA_SIZE]; // receive buffer
char file_buf[FILE_READ_SIZE]; // file buffer
int file_block_size; // read file block size
int socket_fd, new_fd; // listen on socket_fd, serve connections on new_fd
struct addrinfo hints;
struct addrinfo *serv_info; // will point to the results
struct sockaddr_storage their_addr; // connector's address information
socklen_t their_addr_size; // connector's address size
FILE *file_to_send; // file requested by client
string request; // request string
char* file_name; // file name to send
const string get ("GET"); // get string
if(argc != 3) {
cout << "usage: html_sever port root_dir\n";
return 1;
}
// initialize hints struct
memset(&hints, 0, sizeof(hints)); // make sure the struct is empty
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
// getaddrinfo
// TODO: change cout to gai_strerror
if(getaddrinfo(NULL, argv[1], &hints, &serv_info) != 0) { // if unsuccessful
cout << "getaddrinfo error"; // print error
return 1;
}
// create socket
socket_fd = socket(serv_info->ai_family, serv_info->ai_socktype, serv_info->ai_protocol); // open socket
// TODO: add setsockopt (reuse ports)
// bind socket
if(bind(socket_fd, serv_info->ai_addr, serv_info->ai_addrlen) == -1) { // if binding failed
cout << "bind error"; // print error
return 1;
}
// cleanup
freeaddrinfo(serv_info);
// listen on socket
if(listen(socket_fd, BACKLOG) == -1) { // if unsuccessful
cout << "listen error"; // print error
return 1;
}
// TODO: clear zombies
while(1) { // accept loop
their_addr_size = sizeof(their_addr);
new_fd = accept(socket_fd, (struct sockaddr*)&their_addr, &their_addr_size); // accept connection
if(new_fd == -1) { // if unsuccessful
cout << "accept error"; // print error
continue;
}
if((pid = fork()) < 0) // if unsuccessful
cout << "fork error"; // print error
else if(pid == 0) { // child process
close(socket_fd); // close listener
// TODO: refactor to functions
if((bytes_received = recv(new_fd, rec_buf, MAX_DATA_SIZE - 1, 0)) == -1) { // receive request
send(new_fd, HTTP_400, strlen(HTTP_400), 0); // send http400
cout << "receive error"; // print error
exit(1); // terminate child (error)
}
rec_buf[MAX_DATA_SIZE] = '\0'; // close array
request = rec_buf; // convert char array to string
if(!(request.compare(0, 3, get))) { // if received GET request
// get file name (second rec_buf argument)
file_name = strtok(rec_buf, " ");
file_name = strtok(NULL, " ");
// TODO: prepend argv[2] to file_name
file_to_send = fopen(file_name, "r"); // open file to send (read-only)
if(file_to_send == NULL) { // if file not found
send(new_fd, HTTP_404, strlen(HTTP_404), 0); // send http404
cout << "file not found error"; // print error
exit(1); // terminate child (error)
} else {
send(new_fd, HTTP_200, strlen(HTTP_200), 0); // send http200
bzero(file_buf, FILE_READ_SIZE); // zero file buffer
while((file_block_size = fread(file_buf, sizeof(char), FILE_READ_SIZE, file_to_send)) > 0) {
if(send(new_fd, file_buf, file_block_size, 0) < 0) {
cout << "send file error"; // print error
exit(1); // terminate child (error)
}
bzero(file_buf, FILE_READ_SIZE); // clear buffer for new iteration
}
fclose(file_to_send); // close open file
}
} else {
send(new_fd, HTTP_400, strlen(HTTP_400), 0); // send http400
cout << "received data error"; // print error
exit(1); // terminate child (error)
}
close(new_fd); // close socket
exit(0); // terminate child (no-error)
}
close(new_fd); // close new connection on parent
}
return 0;
}