Handles an individual client connection.
228{
229
230 struct timeval stTimeVal;
231 stTimeVal.tv_sec = 5;
232 stTimeVal.tv_usec = 0;
233 setsockopt(nClientFD, SOL_SOCKET, SO_RCVTIMEO, (const char*) &stTimeVal, sizeof stTimeVal);
234
235
236 std::vector<char> vRawRequest;
237 char aChunk[1024];
238 bool bHeaderFound = false;
239
240
241 while (!bHeaderFound && m_bRunning)
242 {
243
244 ssize_t siBytes = recv(nClientFD, aChunk, sizeof(aChunk), 0);
245
246 if (siBytes <= 0)
247 {
248 break;
249 }
250
251 vRawRequest.insert(vRawRequest.end(), aChunk, aChunk + siBytes);
252
253
254 std::string szCurrent(vRawRequest.begin(), vRawRequest.end());
255
256 if (szCurrent.find("\r\n\r\n") != std::string::npos)
257 {
258 bHeaderFound = true;
259 }
260
261
262 if (vRawRequest.size() > 16384)
263 {
264 break;
265 }
266 }
267
268
269 if (bHeaderFound && m_bRunning)
270 {
271
272 std::string szRequest(vRawRequest.begin(), vRawRequest.end());
273 std::string szMethod, szPath, szQuery;
274 std::istringstream stdISS(szRequest);
275 stdISS >> szMethod >> szPath;
276
277
278 size_t siQPos = szPath.find('?');
279
280 if (siQPos != std::string::npos)
281 {
282 szQuery = szPath.substr(siQPos + 1);
283 szPath = szPath.substr(0, siQPos);
284 }
285
286
287 RequestCallback fnCallback = nullptr;
288 std::string szHtmlCopy;
289
290 {
291
292 std::lock_guard<std::mutex> lkDataLock(m_muDataMutex);
293
294 if (m_mGetCallbacks.count(szPath))
295 {
296
297 fnCallback = m_mGetCallbacks[szPath];
298 }
299 else
300 {
301
302 szHtmlCopy = m_szHtmlContent;
303 }
304 }
305
306
307 if (fnCallback)
308 {
309
310 std::vector<char> vData = fnCallback(szQuery);
311
312
313 std::string szContentType = "application/octet-stream";
314 if (szPath.length() >= 3 && szPath.substr(szPath.length() - 3) == ".js")
315 {
316 szContentType = "text/javascript";
317 }
318 else if (szPath.length() >= 4 && szPath.substr(szPath.length() - 4) == ".css")
319 {
320 szContentType = "text/css";
321 }
322
323 std::string szHeader = "HTTP/1.1 200 OK\r\n"
324 "Content-Type: " +
325 szContentType +
326 "\r\n"
327 "Access-Control-Allow-Origin: *\r\n"
328 "Content-Length: " +
329 std::to_string(vData.size()) +
330 "\r\n"
331 "Connection: close\r\n\r\n";
332
333 send(nClientFD, szHeader.c_str(), szHeader.size(), MSG_NOSIGNAL);
334
335
336 size_t siRemaining = vData.size();
337 size_t siSent = 0;
338 while (siRemaining > 0 && m_bRunning)
339 {
340
341 size_t siChunk = (siRemaining > 65536) ? 65536 : siRemaining;
342 ssize_t result = send(nClientFD, vData.data() + siSent, siChunk, MSG_NOSIGNAL);
343
344 if (result <= 0)
345 {
346 break;
347 }
348
349
350 siSent += result;
351 siRemaining -= result;
352 }
353 }
354
355 else if (szPath == "/" || szPath == "/index.html")
356 {
357
358 std::string szHeader = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: " + std::to_string(szHtmlCopy.size()) + "\r\nConnection: close\r\n\r\n";
359 send(nClientFD, szHeader.c_str(), szHeader.size(), MSG_NOSIGNAL);
360 send(nClientFD, szHtmlCopy.c_str(), szHtmlCopy.size(), MSG_NOSIGNAL);
361 }
362
363 else
364 {
365
366 std::string szResp = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n";
367 send(nClientFD, szResp.c_str(), szResp.size(), MSG_NOSIGNAL);
368 }
369 }
370
371
372 close(nClientFD);
373}