Skip to content

Commit 398175d

Browse files
committed
Network functionality complete. Initial connection of Resource manager, HTTP request parsing, and response sending. Need to include HTTP1.1 expected headers
1 parent b95ef8a commit 398175d

File tree

7 files changed

+246
-129
lines changed

7 files changed

+246
-129
lines changed

HTTPServer.cpp

Lines changed: 167 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ HTTPServer::HTTPServer() {
99
memset(&serverAddr, 0, sizeof(serverAddr)); // clear the struct
1010
keepRunning = false;
1111

12-
// Create a resource manager managing the base path ./
12+
// Create a resource manager managing the VIRTUAL base path ./
1313
resMgr = new ResourceManager("./", true);
1414

1515
// Instance clientMap, relates Socket Descriptor to pointer to Client object
@@ -41,7 +41,7 @@ bool HTTPServer::initSocket(int port) {
4141
// Create a handle for the listening socket, TCP
4242
listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
4343
if(listenSocket == INVALID_SOCKET) {
44-
printf("Could not create socket!\n");
44+
cout << "Could not create socket!" << endl;
4545
return false;
4646
}
4747

@@ -52,14 +52,14 @@ bool HTTPServer::initSocket(int port) {
5252

5353
// Bind: Assign the address to the socket
5454
if(bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
55-
printf("Failed to bind to the address!\n");
55+
cout << "Failed to bind to the address!" << endl;
5656
return false;
5757
}
5858

5959
// Listen: Put the socket in a listening state, ready to accept connections
6060
// Accept a backlog of the OS Maximum connections in the queue
6161
if(listen(listenSocket, SOMAXCONN) != 0) {
62-
printf("Failed to put the socket in a listening state\n");
62+
cout << "Failed to put the socket in a listening state" << endl;
6363
return false;
6464
}
6565

@@ -93,28 +93,68 @@ void HTTPServer::closeSockets() {
9393
listenSocket = INVALID_SOCKET;
9494
}
9595

96+
/**
97+
* Accept Connection
98+
* When a new connection is detected in runServer() this function is called. This attempts to accept the pending connection, instance a Client object, and add to the client Map
99+
*/
100+
void HTTPServer::acceptConnection() {
101+
// Setup new client with prelim address info
102+
sockaddr_in clientAddr;
103+
int clientAddrLen = sizeof(clientAddr);
104+
SOCKET clfd = INVALID_SOCKET;
105+
106+
// Accept the pending connection and retrive the client descriptor
107+
clfd = accept(listenSocket, (sockaddr*)&clientAddr, (socklen_t*)&clientAddrLen);
108+
if(clfd == INVALID_SOCKET)
109+
return;
110+
111+
// Instance Client object
112+
Client *cl = new Client(clfd, clientAddr);
113+
114+
// Add to the master FD set
115+
FD_SET(clfd, &fd_master);
116+
117+
// If the new client's handle is greater than the max, set the new max
118+
if(clfd > fdmax)
119+
fdmax = clfd;
120+
121+
// Add the client object to the client map
122+
clientMap->insert(std::pair<int, Client*>(clfd, cl));
123+
124+
// Print the client's IP on connect
125+
cout << "[" << cl->getClientIP() << "] connected" << endl;
126+
}
127+
128+
/**
129+
* Run Server
130+
* Main server loop where the socket is initialized and the loop is started, checking for new messages or clients to be read with select()
131+
* and handling them appropriately
132+
*/
96133
void HTTPServer::runServer(int port) {
97134
// Initialize the socket and put it into a listening state
98135
if(!initSocket(port)) {
99-
printf("Failed to start server.\n");
136+
cout << "Failed to start server." << endl;
100137
return;
101138
}
139+
140+
cout << "Server started. Listening on port " << port << "..." << endl;
102141

103142
// Processing loop
104143
while(keepRunning) {
144+
usleep(1000);
145+
105146
// Copy master set into fd_read for processing
106147
fd_read = fd_master;
107148

108149
// Populate read_fd set with client descriptors that are ready to be read
109-
int selret = select(fdmax+1, &fd_read, NULL, NULL, NULL);
110-
if(selret < 0) {
150+
if(select(fdmax+1, &fd_read, NULL, NULL, NULL) < 0) {
111151
//printf("select failed!");
112152
continue;
113153
}
114154

115155
// Loop through all descriptors in the read_fd set and check to see if data needs to be processed
116156
for(int i = 0; i <= fdmax; i++) {
117-
// If i isn't within the set of descriptors to be read, skip it
157+
// Socket i isn't ready to be read (not in the read set), continue
118158
if(!FD_ISSET(i, &fd_read))
119159
continue;
120160

@@ -132,38 +172,6 @@ void HTTPServer::runServer(int port) {
132172
closeSockets();
133173
}
134174

135-
/**
136-
* Accept Connection
137-
* When a new connection is detected in runServer() this function is called. This attempts to accept the pending connection, instance a Client object, and add to the client Map
138-
*/
139-
void HTTPServer::acceptConnection() {
140-
// Setup new client with prelim address info
141-
sockaddr_in clientAddr;
142-
int clientAddrLen = sizeof(clientAddr);
143-
SOCKET clfd = INVALID_SOCKET;
144-
145-
// Accept the pending connection and retrive the client descriptor
146-
clfd = accept(listenSocket, (sockaddr*)&clientAddr, (socklen_t*)&clientAddrLen);
147-
if(clfd == INVALID_SOCKET)
148-
return;
149-
150-
// Instance Client object
151-
Client *cl = new Client(clfd, clientAddr);
152-
153-
// Add to the master FD set
154-
FD_SET(clfd, &fd_master);
155-
156-
// If the new client's handle is greater than the max, set the new max
157-
if(clfd > fdmax)
158-
fdmax = clfd;
159-
160-
// Add the client object to the client map
161-
clientMap->insert(std::pair<int, Client*>(clfd, cl));
162-
163-
// Print the client's IP on connect
164-
printf("%s has connected\n", cl->getClientIP());
165-
}
166-
167175
/**
168176
* Get Client
169177
* Lookup client based on the socket descriptor number in the clientMap
@@ -208,12 +216,20 @@ void HTTPServer::disconnectClient(Client *cl) {
208216

209217
// http://www.yolinux.com/TUTORIALS/Sockets.html#TIPS
210218

219+
/**
220+
* Handle Client
221+
* Recieve data from a client that has indicated (via select()) that it has data waiting. Pass recv'd data to handleRequest()
222+
* Also detect any errors in the state of the socket
223+
*
224+
* @param cl Pointer to Client that sent the data
225+
*/
211226
void HTTPServer::handleClient(Client *cl) {
212227
if (cl == NULL)
213228
return;
214229

230+
HTTPRequest* req;
215231
size_t dataLen = 1300;
216-
char *pData = new char[dataLen];
232+
char* pData = new char[dataLen];
217233

218234
// Receive data on the wire into pData
219235
/* TODO: Figure out what flags need to be set */
@@ -223,56 +239,45 @@ void HTTPServer::handleClient(Client *cl) {
223239
// Determine state of the client socket and act on it
224240
if(lenRecv == 0) {
225241
// Client closed the connection
226-
printf("Client[%s] has opted to close the connection\n", cl->getClientIP());
242+
cout << "[" << cl->getClientIP() << "] has opted to close the connection" << endl;
227243
disconnectClient(cl);
228244
} else if(lenRecv < 0) {
229245
// Something went wrong with the connection
230246
// TODO: check perror() for the specific error message
231247
disconnectClient(cl);
232248
} else {
233-
// Print the data the client sent (in ascii)
234-
printf("%s: \n", cl->getClientIP());
235-
ascii_print(pData, (int)lenRecv);
249+
// Data received
250+
cout << "[" << cl->getClientIP() << "] " << lenRecv << " bytes received" << endl;
236251

237-
// Add the packet data to a string and pass to processRequest to serve the request
238-
string r;
239-
r.append(pData);
240-
handleRequest(cl, r);
252+
// Place the data in an HTTPRequest and pass it to handleRequest for processing
253+
req = new HTTPRequest((byte*)pData, lenRecv);
254+
handleRequest(cl, req);
255+
delete req;
241256
}
242257

243258
delete [] pData;
244259
}
245260

246-
void HTTPServer::sendResponse(Client *cl, HTTPResponse *res) {
247-
size_t dataLen = 0;
248-
char *sData = NULL;
249-
std::string strResp;
250-
251-
// Get the string response, allocate memory for the response
252-
strResp = res->generateResponse();
253-
dataLen = strlen(strResp.c_str());
254-
sData = new char[dataLen];
255-
256-
// Send the data over the wire
257-
send(cl->getSocket(), sData, dataLen, 0);
258-
259-
// Delete the allocated space for the response
260-
if(sData != NULL)
261-
delete sData;
262-
}
263-
264-
void HTTPServer::handleRequest(Client *cl, string requestStr) {
265-
// Create an HTTPRequest object to consume the requestStr
266-
HTTPRequest *req = new HTTPRequest(requestStr);
267-
if(req == NULL)
268-
return;
269-
270-
HTTPResponse *res = NULL;
271-
272-
// If there was a parse error, report it and send the appropriate error in response
273-
if(req->hasParseError()) {
274-
printf("[%s] There was an error processing the request of type: %i\n", cl->getClientIP(), req->getMethod());
275-
return;
261+
/**
262+
* Handle Request
263+
* Process an incoming request from a Client. Send request off to appropriate handler function
264+
* that corresponds to an HTTP operation (GET, HEAD etc) :)
265+
*
266+
* @param cl Client object where request originated from
267+
* @param req HTTPRequest object filled with raw packet data
268+
*/
269+
void HTTPServer::handleRequest(Client *cl, HTTPRequest* req) {
270+
HTTPResponse* res = NULL;
271+
bool dcClient = false;
272+
273+
// Parse the request
274+
// If there's an error, report it and send the appropriate error in response
275+
if(!req->parse()) {
276+
cout << "[" << cl->getClientIP() << "] There was an error processing the request of type: " << req->methodIntToStr(req->getMethod()) << endl;
277+
cout << req->getParseError() << endl;
278+
// TODO: Send appropriate HTTP error message
279+
disconnectClient(cl);
280+
return;
276281
}
277282

278283
// Send the request to the correct handler function
@@ -284,26 +289,97 @@ void HTTPServer::handleRequest(Client *cl, string requestStr) {
284289
res = handleGet(cl, req);
285290
break;
286291
default:
287-
printf("[%s] Could not handle or determine request of type: %i\n", cl->getClientIP(), req->getMethod());
292+
cout << cl->getClientIP() << ": Could not handle or determine request of type " << req->methodIntToStr(req->getMethod()) << endl;
288293
break;
289294
}
290-
291-
// Send the built response to the client
292-
if(res != NULL)
293-
sendResponse(cl, res);
294-
295-
// Free memory consumed by req and response object
296-
delete req;
297-
if(res != NULL)
298-
delete res;
295+
296+
// If a response could not be built, send a 500 (internal server error)
297+
if(res == NULL) {
298+
res = new HTTPResponse();
299+
res->setStatus(Status(SERVER_ERROR));
300+
std::string body = res->getStatusStr();
301+
res->setData((byte*)body.c_str(), body.size());
302+
dcClient = true;
303+
}
304+
305+
// Send the built response to the client
306+
sendResponse(cl, res, dcClient);
307+
308+
delete res;
299309
}
300310

311+
/**
312+
* Handle Get
313+
* Process a GET request to provide the client with an appropriate response
314+
*
315+
* @param cl Client requesting the resource
316+
* @param req State of the request
317+
*/
301318
HTTPResponse* HTTPServer::handleGet(Client *cl, HTTPRequest *req) {
302-
303-
return NULL;
319+
HTTPResponse* res = NULL;
320+
321+
// Check if the requested resource exists
322+
std::string uri = req->getRequestUri();
323+
Resource* r = resMgr->getResource(uri);
324+
if(r != NULL) { // Exists
325+
326+
} else { // Not found
327+
res = new HTTPResponse();
328+
res->setStatus(Status(NOT_FOUND));
329+
std::string body = res->getStatusStr();
330+
res->setData((byte*)body.c_str(), body.size());
331+
}
332+
333+
return res;
304334
}
305335

336+
/**
337+
* Handle Head
338+
* Process a HEAD request to provide the client with an appropriate response
339+
*
340+
* @param cl Client requesting the resource
341+
* @param req State of the request
342+
*/
306343
HTTPResponse* HTTPServer::handleHead(Client *cl, HTTPRequest *req) {
344+
HTTPResponse* res = NULL;
345+
307346
return NULL;
308347
}
309348

349+
/**
350+
* Send Response
351+
* Send HTTPResponse packet data to a particular Client
352+
*
353+
* @param cl Client to send data to
354+
* @param buf ByteBuffer containing data to be sent
355+
* @param disconnect Should the server disconnect the client after sending (Optional, default = false)
356+
*/
357+
void HTTPServer::sendResponse(Client* cl, HTTPResponse* res, bool disconnect) {
358+
// Get raw data by creating the response (pData will be cleaned up by the response obj)
359+
byte* pData = res->create();
360+
361+
// Retrieve sizes
362+
size_t totalSent = 0, bytesLeft = res->size(), dataLen = res->size();
363+
ssize_t n = 0;
364+
365+
// Solution to deal with partials sends...loop till totalSent matches dataLen
366+
while(totalSent < dataLen) {
367+
n = send(cl->getSocket(), pData+totalSent, bytesLeft, 0);
368+
369+
// Client closed the connection
370+
if(n < 0) {
371+
cout << "[" << cl->getClientIP() << "] has disconnected." << endl;
372+
disconnectClient(cl);
373+
break;
374+
}
375+
376+
// Adjust byte count after a successful send
377+
totalSent += n;
378+
bytesLeft -= n;
379+
}
380+
381+
if(disconnect)
382+
disconnectClient(cl);
383+
}
384+
385+

0 commit comments

Comments
 (0)