1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS HTTP Daemon 4 * FILE: httpd.cpp 5 * PURPOSE: HTTP daemon 6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 7 * REVISIONS: 8 * CSH 01/09/2000 Created 9 */ 10 #include <debug.h> 11 #include <new> 12 #include <malloc.h> 13 #include <string.h> 14 #include <config.h> 15 #include <httpd.h> 16 #include <error.h> 17 18 using namespace std; 19 20 CHAR HttpMsg400[] = "<HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n\r<BODY><H1>400 Bad Request</H1>\n\rThe request had bad syntax.<BR>\n\r</BODY>\n\r\n\r"; 21 CHAR HttpMsg404[] = "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n\r<BODY><H1>404 Not Found</H1>\n\rThe requested URL was not found on this server.<BR>\n\r</BODY>\n\r\n\r"; 22 CHAR HttpMsg405[] = "<HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>\n\r<BODY><H1>405 Method Not Allowed</H1>\n\rThe requested method is not supported on this server.<BR>\n\r</BODY>\n\r\n\r"; 23 CHAR HttpMsg500[] = "<HEAD><TITLE>500 Internal Server Error</TITLE></HEAD>\n\r<BODY><H1>500 Internal Server Error</H1>\n\rAn internal error occurred.<BR>\n\r</BODY>\n\r\n\r"; 24 CHAR HttpMsg501[] = "<HEAD><TITLE>501 Not Implemented</TITLE></HEAD>\n\r<BODY><H1>501 Not Implemented</H1>\n\rThe feature is not implemented.<BR>\n\r</BODY>\n\r\n\r"; 25 26 27 // *************************** CHttpClient *************************** 28 29 // Default constructor 30 CHttpClient::CHttpClient() 31 { 32 } 33 34 // Constructor with server socket as starter value 35 CHttpClient::CHttpClient(CServerSocket *serversocket) 36 { 37 ServerSocket = serversocket; 38 } 39 40 // Split URIs into its parts (ie. |http://|www.host.com|/resource|?parameters|) 41 VOID CHttpClient::SplitUri(LPSTR lpsUri, LPSTR lpsHost, LPSTR lpsResource, LPSTR lpsParams) 42 { 43 LPSTR lpsPos; 44 LPSTR lpsStr; 45 UINT i; 46 47 strcpy(lpsHost, ""); 48 strcpy(lpsResource, ""); 49 strcpy(lpsParams, ""); 50 51 lpsPos = strstr(lpsUri, "://"); 52 if (lpsPos != NULL) 53 lpsStr = &lpsPos[3]; 54 else 55 lpsStr = lpsUri; 56 57 lpsPos = strstr(lpsStr, "/"); 58 if (lpsPos != NULL) { 59 strncat(lpsHost, lpsPos, lpsPos - lpsStr); 60 lpsStr = &lpsPos[1]; 61 62 lpsPos = strstr(lpsStr, "?"); 63 if (lpsPos != NULL) { 64 strncat(lpsResource, lpsStr, lpsPos - lpsStr); 65 strcpy(lpsParams, &lpsPos[1]); 66 } else { 67 strcpy(lpsResource, lpsStr); 68 strcpy(lpsParams, ""); 69 } 70 71 // Replace "/" with "\" 72 for (i = 0; i < strlen(lpsResource); i++) { 73 if (lpsResource[i] == '/') 74 lpsResource[i] = '\\'; 75 } 76 } 77 } 78 79 // Split resource into its parts (ie. |/path/|filename|.extension|) 80 VOID CHttpClient::SplitResource(LPSTR lpsResource, LPSTR lpsPath, LPSTR lpsFilename, LPSTR lpsExtension) 81 { 82 INT i,len,fileptr,extptr; 83 84 strcpy(lpsPath, ""); 85 strcpy(lpsFilename, ""); 86 strcpy(lpsExtension, ""); 87 88 len = strlen(lpsResource); 89 if (len != 0) { 90 if (lpsResource[len - 1] == '/') { 91 // There is only a path 92 strcpy(lpsPath, lpsResource); 93 } else { 94 // Find extension 95 i = len - 1; 96 while ((i >= 0) && (lpsResource[i] != '.')) i--; 97 extptr = i; 98 while ((i >= 0) && (lpsResource[i] != '/')) i--; 99 if (i > 0) { 100 // There is at least one directory in the path (besides root directory) 101 fileptr = i + 1; 102 strncat(lpsPath, lpsResource, fileptr); 103 } else 104 fileptr = 1; 105 106 // Get filename and possibly extension 107 if (extptr != 0) { 108 strncat(lpsFilename, &lpsResource[fileptr], extptr - fileptr); 109 // Get extension 110 strncat(lpsExtension, &lpsResource[extptr + 1], len - extptr - 1); 111 } else 112 strncat(lpsFilename, &lpsResource[fileptr], len - fileptr); 113 } 114 } 115 } 116 117 // Process HTTP request 118 VOID CHttpClient::ProcessRequest() 119 { 120 CHAR sStr[255]; 121 CHAR sHost[255]; 122 CHAR sResource[255]; 123 CHAR sParams[255]; 124 125 // Which method? 126 switch (Parser.nMethodNo) { 127 case hmGET: { 128 SplitUri(Parser.sUri, sHost, sResource, sParams); 129 130 // Default resource? 131 if (strlen(sResource) == 0) { 132 CIterator<LPSTR> *i = pConfiguration->GetDefaultResources()->CreateIterator(); 133 134 // FIXME: All default resources should be tried 135 // Iterate through all strings 136 //for (i->First(); !i->IsDone(); i->Next()) 137 i->First(); 138 if (!i->IsDone()) { 139 strcat(sResource, i->CurrentItem()); 140 delete i; 141 } else { 142 // File not found 143 Report("404 Not Found", HttpMsg404); 144 break; 145 } 146 } 147 strcpy(sStr, pConfiguration->GetHttpBase()); 148 strcat(sStr, sResource); 149 SendFile(sStr); 150 break; 151 } 152 default: { 153 // Method is not implemented 154 Report("501 Not Implemented", HttpMsg501); 155 } 156 } 157 } 158 159 // Send a file to socket 160 VOID CHttpClient::SendFile(LPSTR lpsFilename) 161 { 162 CHAR str[255]; 163 CHAR str2[32]; 164 union BigNum { 165 // unsigned __int64 Big; 166 unsigned long long Big; 167 struct { 168 DWORD Low; 169 DWORD High; 170 } u; 171 } nTotalBytes; 172 DWORD nBytesToRead; 173 DWORD nBytesRead; 174 BOOL bStatus; 175 176 // Try to open file 177 hFile = CreateFileA(lpsFilename, 178 GENERIC_READ, // Open for reading 179 FILE_SHARE_READ, // Share for reading 180 NULL, // No security 181 OPEN_EXISTING, // Existing file only 182 FILE_ATTRIBUTE_NORMAL, // Normal file 183 NULL); // No attr. template 184 if (hFile == INVALID_HANDLE_VALUE) { 185 // File not found 186 Report("404 Not Found", HttpMsg404); 187 return; 188 } 189 // Get file size 190 nTotalBytes.u.Low = GetFileSize(hFile, &nTotalBytes.u.High); 191 if ((nTotalBytes.u.Low == 0xFFFFFFFF) && ((GetLastError()) != NO_ERROR)) { 192 // Internal server error 193 Report("500 Internal Server Error", HttpMsg500); 194 // Close file 195 CloseHandle(hFile); 196 return; 197 } 198 199 // Determine buffer size 200 if (nTotalBytes.Big < 65536) 201 nBufferSize = 1024; 202 else 203 nBufferSize = 32768; 204 // Allocate memory on heap 205 lpsBuffer = (PCHAR) malloc(nBufferSize); 206 207 if (lpsBuffer == NULL) { 208 // Internal server error 209 Report("500 Internal Server Error", HttpMsg500); 210 // Close file 211 CloseHandle(hFile); 212 return; 213 } 214 215 SendText("HTTP/1.1 200 OK"); 216 SendText("Server: ROSHTTPD"); 217 SendText("MIME-version: 1.0"); 218 SendText("Content-Type: text/plain"); 219 SendText("Accept-Ranges: bytes"); 220 strcpy(str, "Content-Length: "); 221 _itoa(nTotalBytes.u.Low, str2, 10); 222 strcat(str, str2); 223 SendText(str); 224 SendText(""); 225 // Read and transmit file 226 nTotalRead = 0; 227 nFileSize = nTotalBytes.Big; 228 bStop = FALSE; 229 230 fd_set wfds; 231 FD_ZERO(&wfds); 232 FD_SET(Socket, &wfds); 233 do { 234 MessageLoop(); 235 236 if (nTotalRead + nBufferSize < nFileSize) 237 nBytesToRead = nBufferSize; 238 else nBytesToRead = nFileSize - nTotalRead; 239 240 bStatus = ReadFile(hFile, lpsBuffer, nBytesToRead, &nBytesRead, NULL); 241 if (bStatus) { 242 select(0, NULL, &wfds, NULL, NULL); 243 bStatus = (Transmit(lpsBuffer, nBytesRead) == (INT)nBytesRead); 244 nTotalRead += nBytesRead; 245 } 246 } while ((!bStop) && (bStatus) && (nTotalRead < nFileSize)); 247 248 if (bStatus) 249 SendText(""); 250 else 251 // We can't send an error message here as we are in the process of sending a file. 252 // We have to terminate the connection instead 253 Close(); 254 255 // Free allocated memory 256 free(lpsBuffer); 257 258 // Close file 259 CloseHandle(hFile); 260 } 261 262 // Report something to client 263 VOID CHttpClient::Report(LPCSTR lpsCode, LPSTR lpsStr) 264 { 265 CHAR sTmp[128]; 266 CHAR sTmp2[16]; 267 268 strcpy(sTmp, "HTTP/1.1 "); 269 strcat(sTmp, lpsCode); 270 SendText(sTmp); 271 SendText("Server: ROSHTTPD"); 272 SendText("MIME-version: 1.0"); 273 SendText("Content-Type: text/html"); 274 SendText("Accept-Ranges: bytes"); 275 strcpy(sTmp, "Content-Length: "); 276 if (lpsStr != NULL) { 277 _itoa(strlen(lpsStr), sTmp2, 10); 278 strcat(sTmp, sTmp2); 279 } else 280 strcat(sTmp, "0"); 281 SendText(sTmp); 282 SendText(""); 283 if (lpsStr != NULL) 284 SendText(lpsStr); 285 SendText(""); 286 } 287 288 // OnRead event handler 289 VOID CHttpClient::OnRead() 290 { 291 LONG nCount; 292 293 nCount = Receive((LPSTR) &Parser.sBuffer[Parser.nHead], 294 sizeof(Parser.sBuffer) - Parser.nHead); 295 296 Parser.nHead += nCount; 297 if (Parser.nHead >= sizeof(Parser.sBuffer)) 298 Parser.nHead = 0; 299 300 if (Parser.Complete()) { 301 ProcessRequest(); 302 } 303 304 if (Parser.bUnknownMethod) { 305 // Method Not Allowed 306 Report("405 Method Not Allowed", HttpMsg405); 307 // Terminate connection 308 Close(); 309 } 310 } 311 /* 312 // OnWrite event handler 313 VOID CHttpClient::OnWrite() 314 { 315 DWORD nBytesToRead; 316 DWORD nBytesRead; 317 318 OutputDebugString(TS("Can write\n")); 319 320 if (bSendingFile) { 321 if (nTotalRead + nBufferSize < nFileSize) 322 nBytesToRead = nBufferSize; 323 else nBytesToRead = nFileSize - nTotalRead; 324 325 bError = ReadFile(hFile, Buffer, nBytesToRead, &nBytesRead, NULL); 326 if (!bError) { 327 Transmit(Buffer, nBytesRead); 328 nTotalRead += nBytesRead; 329 } 330 } 331 } 332 */ 333 // OnClose event handler 334 VOID CHttpClient::OnClose() 335 { 336 // Stop sending file if we are doing that now 337 bStop = TRUE; 338 } 339 340 341 // ************************ CHttpClientThread ************************ 342 343 // Constructor with client socket as starter value 344 CHttpClientThread::CHttpClientThread(LPCServerClientSocket lpSocket) 345 { 346 ClientSocket = lpSocket; 347 } 348 349 // Execute client thread code 350 VOID CHttpClientThread::Execute() 351 { 352 MSG Msg; 353 354 while (!Terminated()) { 355 (( CHttpClient *) ClientSocket)->MessageLoop(); 356 if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE) != 0) { 357 switch (Msg.message) { 358 case HTTPD_START: { 359 // TODO: Start thread 360 break; 361 } 362 case HTTPD_STOP: { 363 // TODO: Stop thread 364 break; 365 } 366 default: 367 DispatchMessage(&Msg); 368 } 369 370 } 371 } 372 373 if (ClientSocket != NULL) { 374 delete ClientSocket; 375 ClientSocket = NULL; 376 } 377 } 378 379 380 // *************************** CHttpDaemon *************************** 381 382 // Default constructor 383 CHttpDaemon::CHttpDaemon() 384 { 385 State = hsStopped; 386 Start(); 387 } 388 389 // Default destructor 390 CHttpDaemon::~CHttpDaemon() 391 { 392 if (State==hsRunning) 393 Stop(); 394 } 395 396 // Return daemon state 397 HTTPdState CHttpDaemon::GetState() const 398 { 399 return State; 400 } 401 402 // Start HTTP daemon 403 BOOL CHttpDaemon::Start() 404 { 405 assert(State==hsStopped); 406 407 SetPort(pConfiguration->GetPort()); 408 409 Open(); 410 411 State = hsRunning; 412 413 return TRUE; 414 } 415 416 // Stop HTTP daemon 417 BOOL CHttpDaemon::Stop() 418 { 419 assert(State==hsRunning); 420 421 Close(); 422 423 State = hsStopped; 424 425 return TRUE; 426 } 427 428 // OnGetSocket event handler 429 LPCServerClientSocket CHttpDaemon::OnGetSocket(LPCServerSocket lpServerSocket) 430 { 431 return new CHttpClient(lpServerSocket); 432 } 433 434 // OnGetThread event handler 435 LPCServerClientThread CHttpDaemon::OnGetThread(LPCServerClientSocket lpSocket) 436 { 437 return new CHttpClientThread(lpSocket); 438 } 439 440 // OnAccept event handler 441 VOID CHttpDaemon::OnAccept(LPCServerClientThread lpThread) 442 { 443 } 444 445 446 // ************************ CHttpDaemonThread ************************ 447 448 // Execute daemon thread code 449 VOID CHttpDaemonThread::Execute() 450 { 451 MSG Msg; 452 453 try { 454 Daemon = NULL; 455 Daemon = new CHttpDaemon; 456 457 while (!Terminated()) { 458 Daemon->MessageLoop(); 459 if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE) != 0) { 460 switch (Msg.message) { 461 case HTTPD_START: { 462 if (Daemon->GetState() == hsStopped) 463 Daemon->Start(); 464 break; 465 } 466 case HTTPD_STOP: { 467 if (Daemon->GetState() == hsRunning) 468 Daemon->Stop(); 469 break; 470 } 471 case HTTPD_SUSPEND: { 472 if (Daemon->GetState() == hsRunning){} 473 // FIXME: Suspend service 474 break; 475 } 476 case HTTPD_RESUME: { 477 if (Daemon->GetState() != hsSuspended){} 478 // FIXME: Resume service 479 break; 480 } 481 default: 482 DispatchMessage(&Msg); 483 } 484 } 485 } 486 delete Daemon; 487 } catch (ESocket e) { 488 ReportErrorStr(e.what()); 489 } catch (bad_alloc&) { 490 ReportErrorStr(TS("Insufficient resources.")); 491 } 492 } 493