/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS HTTP Daemon * FILE: httpd.cpp * PURPOSE: HTTP daemon * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) * REVISIONS: * CSH 01/09/2000 Created */ #include #include #include #include #include #include #include using namespace std; CHAR HttpMsg400[] = "400 Bad Request\n\r

400 Bad Request

\n\rThe request had bad syntax.
\n\r\n\r\n\r"; CHAR HttpMsg404[] = "404 Not Found\n\r

404 Not Found

\n\rThe requested URL was not found on this server.
\n\r\n\r\n\r"; CHAR HttpMsg405[] = "405 Method Not Allowed\n\r

405 Method Not Allowed

\n\rThe requested method is not supported on this server.
\n\r\n\r\n\r"; CHAR HttpMsg500[] = "500 Internal Server Error\n\r

500 Internal Server Error

\n\rAn internal error occurred.
\n\r\n\r\n\r"; CHAR HttpMsg501[] = "501 Not Implemented\n\r

501 Not Implemented

\n\rThe feature is not implemented.
\n\r\n\r\n\r"; // *************************** CHttpClient *************************** // Default constructor CHttpClient::CHttpClient() { } // Constructor with server socket as starter value CHttpClient::CHttpClient(CServerSocket *serversocket) { ServerSocket = serversocket; } // Split URIs into its parts (ie. |http://|www.host.com|/resource|?parameters|) VOID CHttpClient::SplitUri(LPSTR lpsUri, LPSTR lpsHost, LPSTR lpsResource, LPSTR lpsParams) { LPSTR lpsPos; LPSTR lpsStr; UINT i; strcpy(lpsHost, ""); strcpy(lpsResource, ""); strcpy(lpsParams, ""); lpsPos = strstr(lpsUri, "://"); if (lpsPos != NULL) lpsStr = &lpsPos[3]; else lpsStr = lpsUri; lpsPos = strstr(lpsStr, "/"); if (lpsPos != NULL) { strncat(lpsHost, lpsPos, lpsPos - lpsStr); lpsStr = &lpsPos[1]; lpsPos = strstr(lpsStr, "?"); if (lpsPos != NULL) { strncat(lpsResource, lpsStr, lpsPos - lpsStr); strcpy(lpsParams, &lpsPos[1]); } else { strcpy(lpsResource, lpsStr); strcpy(lpsParams, ""); } // Replace "/" with "\" for (i = 0; i < strlen(lpsResource); i++) { if (lpsResource[i] == '/') lpsResource[i] = '\\'; } } } // Split resource into its parts (ie. |/path/|filename|.extension|) VOID CHttpClient::SplitResource(LPSTR lpsResource, LPSTR lpsPath, LPSTR lpsFilename, LPSTR lpsExtension) { INT i,len,fileptr,extptr; strcpy(lpsPath, ""); strcpy(lpsFilename, ""); strcpy(lpsExtension, ""); len = strlen(lpsResource); if (len != 0) { if (lpsResource[len - 1] == '/') { // There is only a path strcpy(lpsPath, lpsResource); } else { // Find extension i = len - 1; while ((i >= 0) && (lpsResource[i] != '.')) i--; extptr = i; while ((i >= 0) && (lpsResource[i] != '/')) i--; if (i > 0) { // There is at least one directory in the path (besides root directory) fileptr = i + 1; strncat(lpsPath, lpsResource, fileptr); } else fileptr = 1; // Get filename and possibly extension if (extptr != 0) { strncat(lpsFilename, &lpsResource[fileptr], extptr - fileptr); // Get extension strncat(lpsExtension, &lpsResource[extptr + 1], len - extptr - 1); } else strncat(lpsFilename, &lpsResource[fileptr], len - fileptr); } } } // Process HTTP request VOID CHttpClient::ProcessRequest() { CHAR sStr[255]; CHAR sHost[255]; CHAR sResource[255]; CHAR sParams[255]; // Which method? switch (Parser.nMethodNo) { case hmGET: { SplitUri(Parser.sUri, sHost, sResource, sParams); // Default resource? if (strlen(sResource) == 0) { CIterator *i = pConfiguration->GetDefaultResources()->CreateIterator(); // FIXME: All default resources should be tried // Iterate through all strings //for (i->First(); !i->IsDone(); i->Next()) i->First(); if (!i->IsDone()) { strcat(sResource, i->CurrentItem()); delete i; } else { // File not found Report("404 Not Found", HttpMsg404); break; } } strcpy(sStr, pConfiguration->GetHttpBase()); strcat(sStr, sResource); SendFile(sStr); break; } default: { // Method is not implemented Report("501 Not Implemented", HttpMsg501); } } } // Send a file to socket VOID CHttpClient::SendFile(LPSTR lpsFilename) { CHAR str[255]; CHAR str2[32]; union BigNum { // unsigned __int64 Big; unsigned long long Big; struct { DWORD Low; DWORD High; } u; } nTotalBytes; DWORD nBytesToRead; DWORD nBytesRead; BOOL bStatus; // Try to open file hFile = CreateFileA(lpsFilename, GENERIC_READ, // Open for reading FILE_SHARE_READ, // Share for reading NULL, // No security OPEN_EXISTING, // Existing file only FILE_ATTRIBUTE_NORMAL, // Normal file NULL); // No attr. template if (hFile == INVALID_HANDLE_VALUE) { // File not found Report("404 Not Found", HttpMsg404); return; } // Get file size nTotalBytes.u.Low = GetFileSize(hFile, &nTotalBytes.u.High); if ((nTotalBytes.u.Low == 0xFFFFFFFF) && ((GetLastError()) != NO_ERROR)) { // Internal server error Report("500 Internal Server Error", HttpMsg500); // Close file CloseHandle(hFile); return; } // Determine buffer size if (nTotalBytes.Big < 65536) nBufferSize = 1024; else nBufferSize = 32768; // Allocate memory on heap lpsBuffer = (PCHAR) malloc(nBufferSize); if (lpsBuffer == NULL) { // Internal server error Report("500 Internal Server Error", HttpMsg500); // Close file CloseHandle(hFile); return; } SendText("HTTP/1.1 200 OK"); SendText("Server: ROSHTTPD"); SendText("MIME-version: 1.0"); SendText("Content-Type: text/plain"); SendText("Accept-Ranges: bytes"); strcpy(str, "Content-Length: "); _itoa(nTotalBytes.u.Low, str2, 10); strcat(str, str2); SendText(str); SendText(""); // Read and transmit file nTotalRead = 0; nFileSize = nTotalBytes.Big; bStop = FALSE; fd_set wfds; FD_ZERO(&wfds); FD_SET(Socket, &wfds); do { MessageLoop(); if (nTotalRead + nBufferSize < nFileSize) nBytesToRead = nBufferSize; else nBytesToRead = nFileSize - nTotalRead; bStatus = ReadFile(hFile, lpsBuffer, nBytesToRead, &nBytesRead, NULL); if (bStatus) { select(0, NULL, &wfds, NULL, NULL); bStatus = (Transmit(lpsBuffer, nBytesRead) == (INT)nBytesRead); nTotalRead += nBytesRead; } } while ((!bStop) && (bStatus) && (nTotalRead < nFileSize)); if (bStatus) SendText(""); else // We can't send an error message here as we are in the process of sending a file. // We have to terminate the connection instead Close(); // Free allocated memory free(lpsBuffer); // Close file CloseHandle(hFile); } // Report something to client VOID CHttpClient::Report(LPCSTR lpsCode, LPSTR lpsStr) { CHAR sTmp[128]; CHAR sTmp2[16]; strcpy(sTmp, "HTTP/1.1 "); strcat(sTmp, lpsCode); SendText(sTmp); SendText("Server: ROSHTTPD"); SendText("MIME-version: 1.0"); SendText("Content-Type: text/html"); SendText("Accept-Ranges: bytes"); strcpy(sTmp, "Content-Length: "); if (lpsStr != NULL) { _itoa(strlen(lpsStr), sTmp2, 10); strcat(sTmp, sTmp2); } else strcat(sTmp, "0"); SendText(sTmp); SendText(""); if (lpsStr != NULL) SendText(lpsStr); SendText(""); } // OnRead event handler VOID CHttpClient::OnRead() { LONG nCount; nCount = Receive((LPSTR) &Parser.sBuffer[Parser.nHead], sizeof(Parser.sBuffer) - Parser.nHead); Parser.nHead += nCount; if (Parser.nHead >= sizeof(Parser.sBuffer)) Parser.nHead = 0; if (Parser.Complete()) { ProcessRequest(); } if (Parser.bUnknownMethod) { // Method Not Allowed Report("405 Method Not Allowed", HttpMsg405); // Terminate connection Close(); } } /* // OnWrite event handler VOID CHttpClient::OnWrite() { DWORD nBytesToRead; DWORD nBytesRead; OutputDebugString(TS("Can write\n")); if (bSendingFile) { if (nTotalRead + nBufferSize < nFileSize) nBytesToRead = nBufferSize; else nBytesToRead = nFileSize - nTotalRead; bError = ReadFile(hFile, Buffer, nBytesToRead, &nBytesRead, NULL); if (!bError) { Transmit(Buffer, nBytesRead); nTotalRead += nBytesRead; } } } */ // OnClose event handler VOID CHttpClient::OnClose() { // Stop sending file if we are doing that now bStop = TRUE; } // ************************ CHttpClientThread ************************ // Constructor with client socket as starter value CHttpClientThread::CHttpClientThread(LPCServerClientSocket lpSocket) { ClientSocket = lpSocket; } // Execute client thread code VOID CHttpClientThread::Execute() { MSG Msg; while (!Terminated()) { (( CHttpClient *) ClientSocket)->MessageLoop(); if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE) != 0) { switch (Msg.message) { case HTTPD_START: { // TODO: Start thread break; } case HTTPD_STOP: { // TODO: Stop thread break; } default: DispatchMessage(&Msg); } } } if (ClientSocket != NULL) { delete ClientSocket; ClientSocket = NULL; } } // *************************** CHttpDaemon *************************** // Default constructor CHttpDaemon::CHttpDaemon() { State = hsStopped; Start(); } // Default destructor CHttpDaemon::~CHttpDaemon() { if (State==hsRunning) Stop(); } // Return daemon state HTTPdState CHttpDaemon::GetState() const { return State; } // Start HTTP daemon BOOL CHttpDaemon::Start() { assert(State==hsStopped); SetPort(pConfiguration->GetPort()); Open(); State = hsRunning; return TRUE; } // Stop HTTP daemon BOOL CHttpDaemon::Stop() { assert(State==hsRunning); Close(); State = hsStopped; return TRUE; } // OnGetSocket event handler LPCServerClientSocket CHttpDaemon::OnGetSocket(LPCServerSocket lpServerSocket) { return new CHttpClient(lpServerSocket); } // OnGetThread event handler LPCServerClientThread CHttpDaemon::OnGetThread(LPCServerClientSocket lpSocket) { return new CHttpClientThread(lpSocket); } // OnAccept event handler VOID CHttpDaemon::OnAccept(LPCServerClientThread lpThread) { } // ************************ CHttpDaemonThread ************************ // Execute daemon thread code VOID CHttpDaemonThread::Execute() { MSG Msg; try { Daemon = NULL; Daemon = new CHttpDaemon; while (!Terminated()) { Daemon->MessageLoop(); if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE) != 0) { switch (Msg.message) { case HTTPD_START: { if (Daemon->GetState() == hsStopped) Daemon->Start(); break; } case HTTPD_STOP: { if (Daemon->GetState() == hsRunning) Daemon->Stop(); break; } case HTTPD_SUSPEND: { if (Daemon->GetState() == hsRunning){} // FIXME: Suspend service break; } case HTTPD_RESUME: { if (Daemon->GetState() != hsSuspended){} // FIXME: Resume service break; } default: DispatchMessage(&Msg); } } } delete Daemon; } catch (ESocket e) { ReportErrorStr(e.what()); } catch (bad_alloc e) { ReportErrorStr(TS("Insufficient resources.")); } }