2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS HTTP Daemon
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * CSH 01/09/2000 Created
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";
27 // *************************** CHttpClient ***************************
29 // Default constructor
30 CHttpClient::CHttpClient()
34 // Constructor with server socket as starter value
35 CHttpClient::CHttpClient(CServerSocket *serversocket)
37 ServerSocket = serversocket;
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)
48 strcpy(lpsResource, "");
49 strcpy(lpsParams, "");
51 lpsPos = strstr(lpsUri, "://");
57 lpsPos = strstr(lpsStr, "/");
59 strncat(lpsHost, lpsPos, lpsPos - lpsStr);
62 lpsPos = strstr(lpsStr, "?");
64 strncat(lpsResource, lpsStr, lpsPos - lpsStr);
65 strcpy(lpsParams, &lpsPos[1]);
67 strcpy(lpsResource, lpsStr);
68 strcpy(lpsParams, "");
71 // Replace "/" with "\"
72 for (i = 0; i < strlen(lpsResource); i++) {
73 if (lpsResource[i] == '/')
74 lpsResource[i] = '\\';
79 // Split resource into its parts (ie. |/path/|filename|.extension|)
80 VOID CHttpClient::SplitResource(LPSTR lpsResource, LPSTR lpsPath, LPSTR lpsFilename, LPSTR lpsExtension)
82 INT i,len,fileptr,extptr;
85 strcpy(lpsFilename, "");
86 strcpy(lpsExtension, "");
88 len = strlen(lpsResource);
90 if (lpsResource[len - 1] == '/') {
91 // There is only a path
92 strcpy(lpsPath, lpsResource);
96 while ((i >= 0) && (lpsResource[i] != '.')) i--;
98 while ((i >= 0) && (lpsResource[i] != '/')) i--;
100 // There is at least one directory in the path (besides root directory)
102 strncat(lpsPath, lpsResource, fileptr);
106 // Get filename and possibly extension
108 strncat(lpsFilename, &lpsResource[fileptr], extptr - fileptr);
110 strncat(lpsExtension, &lpsResource[extptr + 1], len - extptr - 1);
112 strncat(lpsFilename, &lpsResource[fileptr], len - fileptr);
117 // Process HTTP request
118 VOID CHttpClient::ProcessRequest()
126 switch (Parser.nMethodNo) {
128 SplitUri(Parser.sUri, sHost, sResource, sParams);
131 if (strlen(sResource) == 0) {
132 CIterator<LPSTR> *i = pConfiguration->GetDefaultResources()->CreateIterator();
134 // FIXME: All default resources should be tried
135 // Iterate through all strings
136 //for (i->First(); !i->IsDone(); i->Next())
139 strcat(sResource, i->CurrentItem());
143 Report("404 Not Found", HttpMsg404);
147 strcpy(sStr, pConfiguration->GetHttpBase());
148 strcat(sStr, sResource);
153 // Method is not implemented
154 Report("501 Not Implemented", HttpMsg501);
159 // Send a file to socket
160 VOID CHttpClient::SendFile(LPSTR lpsFilename)
165 // unsigned __int64 Big;
166 unsigned long long Big;
177 hFile = CreateFileA(lpsFilename,
178 GENERIC_READ, // Open for reading
179 FILE_SHARE_READ, // Share for reading
181 OPEN_EXISTING, // Existing file only
182 FILE_ATTRIBUTE_NORMAL, // Normal file
183 NULL); // No attr. template
184 if (hFile == INVALID_HANDLE_VALUE) {
186 Report("404 Not Found", HttpMsg404);
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);
199 // Determine buffer size
200 if (nTotalBytes.Big < 65536)
204 // Allocate memory on heap
205 lpsBuffer = (PCHAR) malloc(nBufferSize);
207 if (lpsBuffer == NULL) {
208 // Internal server error
209 Report("500 Internal Server Error", HttpMsg500);
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);
225 // Read and transmit file
227 nFileSize = nTotalBytes.Big;
232 FD_SET(Socket, &wfds);
236 if (nTotalRead + nBufferSize < nFileSize)
237 nBytesToRead = nBufferSize;
238 else nBytesToRead = nFileSize - nTotalRead;
240 bStatus = ReadFile(hFile, lpsBuffer, nBytesToRead, &nBytesRead, NULL);
242 select(0, NULL, &wfds, NULL, NULL);
243 bStatus = (Transmit(lpsBuffer, nBytesRead) == (INT)nBytesRead);
244 nTotalRead += nBytesRead;
246 } while ((!bStop) && (bStatus) && (nTotalRead < nFileSize));
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
255 // Free allocated memory
262 // Report something to client
263 VOID CHttpClient::Report(LPSTR lpsCode, LPSTR lpsStr)
268 strcpy(sTmp, "HTTP/1.1 ");
269 strcat(sTmp, lpsCode);
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);
288 // OnRead event handler
289 VOID CHttpClient::OnRead()
293 nCount = Receive((LPSTR) &Parser.sBuffer[Parser.nHead],
294 sizeof(Parser.sBuffer) - Parser.nHead);
296 Parser.nHead += nCount;
297 if (Parser.nHead >= sizeof(Parser.sBuffer))
300 if (Parser.Complete()) {
304 if (Parser.bUnknownMethod) {
305 // Method Not Allowed
306 Report("405 Method Not Allowed", HttpMsg405);
307 // Terminate connection
312 // OnWrite event handler
313 VOID CHttpClient::OnWrite()
318 OutputDebugString(_T("Can write\n"));
321 if (nTotalRead + nBufferSize < nFileSize)
322 nBytesToRead = nBufferSize;
323 else nBytesToRead = nFileSize - nTotalRead;
325 bError = ReadFile(hFile, Buffer, nBytesToRead, &nBytesRead, NULL);
327 Transmit(Buffer, nBytesRead);
328 nTotalRead += nBytesRead;
333 // OnClose event handler
334 VOID CHttpClient::OnClose()
336 // Stop sending file if we are doing that now
341 // ************************ CHttpClientThread ************************
343 // Constructor with client socket as starter value
344 CHttpClientThread::CHttpClientThread(LPCServerClientSocket lpSocket)
346 ClientSocket = lpSocket;
349 // Execute client thread code
350 VOID CHttpClientThread::Execute()
354 while (!Terminated()) {
355 (( CHttpClient *) ClientSocket)->MessageLoop();
356 if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE) != 0) {
357 switch (Msg.message) {
359 // TODO: Start thread
367 DispatchMessage(&Msg);
373 if (ClientSocket != NULL) {
380 // *************************** CHttpDaemon ***************************
382 // Default constructor
383 CHttpDaemon::CHttpDaemon()
389 // Default destructor
390 CHttpDaemon::~CHttpDaemon()
392 if (State==hsRunning)
396 // Return daemon state
397 HTTPdState CHttpDaemon::GetState() const
403 BOOL CHttpDaemon::Start()
405 assert(State==hsStopped);
407 SetPort(pConfiguration->GetPort());
417 BOOL CHttpDaemon::Stop()
419 assert(State==hsRunning);
428 // OnGetSocket event handler
429 LPCServerClientSocket CHttpDaemon::OnGetSocket(LPCServerSocket lpServerSocket)
431 return new CHttpClient(lpServerSocket);
434 // OnGetThread event handler
435 LPCServerClientThread CHttpDaemon::OnGetThread(LPCServerClientSocket lpSocket)
437 return new CHttpClientThread(lpSocket);
440 // OnAccept event handler
441 VOID CHttpDaemon::OnAccept(LPCServerClientThread lpThread)
446 // ************************ CHttpDaemonThread ************************
448 // Execute daemon thread code
449 VOID CHttpDaemonThread::Execute()
455 Daemon = new CHttpDaemon;
457 while (!Terminated()) {
458 Daemon->MessageLoop();
459 if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE) != 0) {
460 switch (Msg.message) {
462 if (Daemon->GetState() == hsStopped)
467 if (Daemon->GetState() == hsRunning)
471 case HTTPD_SUSPEND: {
472 if (Daemon->GetState() == hsRunning){}
473 // FIXME: Suspend service
477 if (Daemon->GetState() != hsSuspended){}
478 // FIXME: Resume service
482 DispatchMessage(&Msg);
487 } catch (ESocket e) {
488 ReportErrorStr(e.what());
489 } catch (bad_alloc e) {
490 ReportErrorStr(TS("Insufficient resources."));