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(TS("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."));