Small HTTP daemon

svn path=/trunk/; revision=1515
This commit is contained in:
Casper Hornstrup 2001-01-14 18:35:17 +00:00
parent 05ccbb2696
commit 52f51dc4b9
18 changed files with 2303 additions and 0 deletions

View file

@ -0,0 +1,94 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: list.cpp
* PURPOSE: A doubly linked list implementation
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/09/2000 Created
* NOTES: The linked list does it's own heap management for
* better performance
* TODO: - InsertBefore(), InsertAfter(), Move()
*/
#include <windows.h>
#include <list.h>
// **************************** CListNode ****************************
HANDLE CListNode::hHeap = NULL;
INT CListNode::nRef = 0;
// Default constructor
CListNode::CListNode()
{
Element = NULL;
Next = NULL;
Prev = NULL;
}
// Constructor with element and next as starter values
CListNode::CListNode(PVOID element, CListNode *next, CListNode *prev)
{
Element = element;
Next = next;
Prev = prev;
}
PVOID CListNode::operator new(/*size_t*/ UINT size)
{
PVOID p;
if (hHeap == NULL) {
SYSTEM_INFO inf;
GetSystemInfo(&inf);
hHeap = HeapCreate(0, inf.dwAllocationGranularity, 0);
}
if ((p = HeapAlloc(hHeap, 0, size)) != NULL)
nRef++;
return p;
}
VOID CListNode::operator delete(PVOID p)
{
if (HeapFree(hHeap, 0, p) != FALSE)
nRef--;
if (nRef == 0) {
HeapDestroy(hHeap);
hHeap = NULL;
}
}
// Set element
VOID CListNode::SetElement(PVOID element)
{
Element = element;
}
// Set pointer to next node in list
VOID CListNode::SetNext(CListNode *next)
{
Next = next;
}
// Set pointer to previous node in list
VOID CListNode::SetPrev(CListNode *prev)
{
Prev = prev;
}
// Get element of node
PVOID CListNode::GetElement()
{
return Element;
}
// Get pointer to next node in list
CListNode *CListNode::GetNext()
{
return Next;
}
// Get pointer to previous node in list
CListNode *CListNode::GetPrev()
{
return Prev;
}

View file

@ -0,0 +1,343 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: socket.cpp
* PURPOSE: Socket classes
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/09/2000 Created
*/
#include <error.h>
#include <socket.h>
#include <iterator.h>
// ***************************** CSocket *****************************
// Default constructor
CSocket::CSocket()
{
Active = FALSE;
Event = WSA_INVALID_EVENT;
Events = 0;
Socket = INVALID_SOCKET;
// INET address family
SockAddrIn.sin_family = AF_INET;
// Any address will do
SockAddrIn.sin_addr.s_addr = INADDR_ANY;
// Convert to network ordering
SockAddrIn.sin_port = htons(0);
}
// Default destructor
CSocket::~CSocket()
{
}
// Return winsock socket handle
SOCKET CSocket::GetSocket()
{
return Socket;
}
// Set winsock socket handle
VOID CSocket::SetSocket(SOCKET socket)
{
Socket = socket;
}
// Return socket address
SOCKADDR_IN CSocket::GetSockAddrIn()
{
return SockAddrIn;
}
// Set socket address
VOID CSocket::SetSockAddrIn(SOCKADDR_IN sockaddrin)
{
SockAddrIn = sockaddrin;
}
// Associate winsock events with socket
VOID CSocket::SetEvents(LONG lEvents)
{
if (Event == WSA_INVALID_EVENT) {
// Create socket event
Event = WSACreateEvent();
if (Event == WSA_INVALID_EVENT)
throw ESocketOpen(TS("Unable to create event."));
}
if (lEvents != Events) {
// Associate network events with socket
if (WSAEventSelect(Socket, Event, lEvents) == SOCKET_ERROR)
throw ESocketOpen(TS("Unable to select socket events."));
Events = lEvents;
}
}
// Return associated winsock events
LONG CSocket::GetEvents()
{
return Events;
}
// Open socket
VOID CSocket::Open()
{
}
// Close socket
VOID CSocket::Close()
{
}
// *********************** CServerClientSocket ***********************
// Constructor with serversocket as parameter
CServerClientSocket::CServerClientSocket(LPCServerSocket lpServerSocket)
{
ServerSocket = lpServerSocket;
}
// Transmit data to socket
INT CServerClientSocket::Transmit( LPSTR lpsBuffer, UINT nLength)
{
return send(Socket, lpsBuffer, nLength, 0);
}
// Send a string to socket
INT CServerClientSocket::SendText( LPSTR lpsText)
{
static CHAR crlf[3] = {0x0D, 0x0A, 0x00};
INT nCount;
nCount = Transmit(lpsText, strlen(lpsText));
nCount += Transmit(crlf, strlen(crlf));
return nCount;
}
// Receive data from socket
INT CServerClientSocket::Receive(LPSTR lpsBuffer, UINT nLength)
{
return recv(Socket, lpsBuffer, nLength, 0);
}
// Process winsock messages if any
VOID CServerClientSocket::MessageLoop()
{
UINT nStatus;
WSANETWORKEVENTS NetworkEvents;
nStatus = WSAWaitForMultipleEvents(1, &Event, FALSE, 0, FALSE);
if ((nStatus == 0) && (WSAEnumNetworkEvents(Socket, Event, &NetworkEvents) != SOCKET_ERROR)) {
if ((NetworkEvents.lNetworkEvents & FD_READ) != 0) {
OnRead();
}
if ((NetworkEvents.lNetworkEvents & FD_CLOSE) != 0) {
OnClose();
}
}
}
// Return server socket that own this socket
LPCServerSocket CServerClientSocket::GetServerSocket()
{
return ServerSocket;
}
// *********************** CServerClientThread ***********************
CServerClientThread::CServerClientThread(LPCServerClientSocket lpSocket)
{
ClientSocket = lpSocket;
}
CServerClientThread::~CServerClientThread()
{
ClientSocket->GetServerSocket()->RemoveClient((LPCServerClientThread) this);
}
// ************************** CServerSocket **************************
// Default constructor
CServerSocket::CServerSocket()
{
}
// Default destructor
CServerSocket::~CServerSocket()
{
if (Active)
Close();
}
// Open server socket so clients can connect
VOID CServerSocket::Open()
{
assert(!Active);
// Convert to network ordering
SockAddrIn.sin_port = htons(Port);
if (Socket == INVALID_SOCKET) {
// Create socket
Socket = socket(AF_INET, SOCK_STREAM, 0);
if (Socket == INVALID_SOCKET)
throw ESocketOpen(TS("Unable to allocate a socket."));
}
// Associate an address with server socket
if (bind(Socket, (struct sockaddr FAR *) &SockAddrIn, sizeof(SockAddrIn)) == SOCKET_ERROR)
throw ESocketOpen(TS("Unable to associate address with socket."));
// Listen for incoming connections
if (listen(Socket, MAX_PENDING_CONNECTS) != 0)
throw ESocketOpen(TS("Unable to listen on socket."));
// Associate network events with socket
SetEvents(FD_ACCEPT | FD_CONNECT | FD_CLOSE);
Active = TRUE;
}
// Close server socket and all current connections
VOID CServerSocket::Close()
{
assert(Active);
if (Event != WSA_INVALID_EVENT) {
// Tell winsock not to notify us about any events
if (WSAEventSelect(Socket, Event, 0) == SOCKET_ERROR)
throw ESocketClose(TS("Unable to select socket events."));
if (!WSACloseEvent(Event))
throw ESocketClose(TS("Unable to close socket event."));
Event = WSA_INVALID_EVENT;
}
CIterator<LPCServerClientThread> *i = Connections.CreateIterator();
// Terminate and free all client threads
for (i->First(); !i->IsDone(); i->Next()) {
//i->CurrentItem()->Terminate();
delete i->CurrentItem();
}
delete i;
Connections.RemoveAll();
closesocket(Socket);
Socket = INVALID_SOCKET;
Active = FALSE;
}
// Set port number to listen on
VOID CServerSocket::SetPort(UINT nPort)
{
assert(!Active);
Port = nPort;
}
// Process messages from winsock if any
VOID CServerSocket::MessageLoop()
{
UINT nStatus;
INT nAddrLen;
SOCKET ClientSocket;
SOCKADDR_IN SockAddrIn;
WSANETWORKEVENTS NetworkEvents;
LPCServerClientSocket lpClient;
LPCServerClientThread lpThread;
nStatus = WSAWaitForMultipleEvents(1, &Event, FALSE, 0, FALSE);
if ((nStatus == 0) && (WSAEnumNetworkEvents(Socket, Event, &NetworkEvents) != SOCKET_ERROR)) {
if ((NetworkEvents.lNetworkEvents & FD_ACCEPT) != 0) {
lpClient = OnGetSocket(this);
nAddrLen = sizeof(SockAddrIn);
ClientSocket = accept(Socket, (SOCKADDR *) &SockAddrIn, &nAddrLen);
if (ClientSocket != INVALID_SOCKET) {
// Set socket handle
lpClient->SetSocket(ClientSocket);
// Set socket address
lpClient->SetSockAddrIn(SockAddrIn);
// Set winsock events
lpClient->SetEvents(FD_READ | FD_CLOSE);
// Create client connection thread
lpThread = OnGetThread(lpClient);
// Add client thread to connection list
InsertClient(lpThread);
// Call OnAccept event handler
OnAccept(lpThread);
} else {
delete lpClient;
lpClient = NULL;
throw ESocketOpen(TS("No more sockets available."));
}
}
/*if ((NetworkEvents.lNetworkEvents & FD_CONNECT) != 0) {
}
if ((NetworkEvents.lNetworkEvents & FD_CLOSE) != 0) {
}*/
}
}
// Insert client into connection list
VOID CServerSocket::InsertClient(LPCServerClientThread lpClient)
{
Connections.Insert(lpClient);
}
// Remove client from connection list
VOID CServerSocket::RemoveClient(LPCServerClientThread lpClient)
{
Connections.Remove(lpClient);
}
// OnGetSocket event handler
LPCServerClientSocket CServerSocket::OnGetSocket(LPCServerSocket lpServerSocket)
{
return NULL;
}
// OnGetThread event handler
LPCServerClientThread CServerSocket::OnGetThread(LPCServerClientSocket lpSocket)
{
return NULL;
}
// Initialize WinSock DLL
VOID InitWinsock()
{
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 0);
if (WSAStartup(wVersionRequested, &wsaData) != 0)
// Return FALSE as we couldn't find a usable WinSock DLL
throw ESocketWinsock(TS("Unable to initialize winsock dll."));
/* Confirm that the WinSock DLL supports 2.0 */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) {
// We couldn't find a usable winsock dll
WSACleanup();
throw ESocketDll(TS("Winsock dll version is not 2.0 or higher."));
}
}
// Deinitialize WinSock DLL
VOID DeinitWinsock()
{
if (WSACleanup() != 0)
throw ESocketWinsock(TS("Unable to deinitialize winsock dll."));
}

View file

@ -0,0 +1,80 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: thread.cpp
* PURPOSE: Generic thread class
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/09/2000 Created
*/
#include <debug.h>
#include <assert.h>
#include <windows.h>
#include <thread.h>
// This is the thread entry code
DWORD WINAPI ThreadEntry(LPVOID parameter)
{
ThreadData *p = (ThreadData*) parameter;
p->ClassPtr->Execute();
SetEvent(p->hFinished);
return 0;
}
// Default constructor
CThread::CThread()
{
bTerminated = FALSE;
// Points to the class that is executed within thread
Data.ClassPtr = this;
// Create synchronization event
Data.hFinished = CreateEvent(NULL, TRUE, FALSE, NULL);
// FIXME: Do some error handling
assert(Data.hFinished != NULL);
// Create thread
hThread = CreateThread(NULL, 0, ThreadEntry, &Data, 0, &dwThreadId);
// FIXME: Do some error handling
assert(hThread != NULL);
}
// Default destructor
CThread::~CThread()
{
if ((hThread != NULL) && (Data.hFinished != NULL)) {
if (!bTerminated)
Terminate();
WaitForSingleObject(Data.hFinished, INFINITE);
CloseHandle(Data.hFinished);
CloseHandle(hThread);
hThread = NULL;
}
}
// Execute thread code
void CThread::Execute()
{
while (!bTerminated) Sleep(0);
}
// Post a message to the thread's message queue
BOOL CThread::PostMessage(UINT Msg, WPARAM wParam, LPARAM lParam)
{
return PostThreadMessage(dwThreadId, Msg, wParam, lParam);
}
// Gracefully terminate thread
void CThread::Terminate()
{
bTerminated = TRUE;
}
// Returns TRUE if thread is terminated, FALSE if not
BOOL CThread::Terminated()
{
return bTerminated;
}

View file

@ -0,0 +1,130 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: config.cpp
* PURPOSE: Daemon configuration
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/09/2000 Created
*/
#include <new>
#include <stdlib.h>
#include <string.h>
#include <config.h>
using namespace std;
LPCConfig pConfiguration;
LPCHttpDaemonThread pDaemonThread;
// Default constructor
CConfig::CConfig()
{
Reset();
}
// Default destructor
CConfig::~CConfig()
{
Clear();
}
// Clear configuration
void CConfig::Reset()
{
MainBase = NULL;
HttpBase = NULL;
DefaultResources.RemoveAll();
}
// Create default configuration. Can throw bad_alloc
void CConfig::Default()
{
Clear();
MainBase = (LPWSTR)_wcsdup(dcfgMainBase);
HttpBase = _strdup(dcfgHttpBase);
LPSTR lpsStr;
try {
lpsStr = _strdup(dcfgDefaultResource);
DefaultResources.Insert(lpsStr);
} catch (bad_alloc e) {
free((void *)lpsStr);
Clear();
throw;
}
Port = dcfgDefaultPort;
}
// Clear configuration
void CConfig::Clear()
{
if (MainBase != NULL)
free((void *)MainBase);
if (HttpBase != NULL)
free((void *)HttpBase);
// Free memory for all strings
CIterator<LPSTR> *i = DefaultResources.CreateIterator();
for (i->First(); !i->IsDone(); i->Next())
free((void *)i->CurrentItem());
delete i;
Reset();
}
// Load configuration
BOOL CConfig::Load()
{
Default();
return TRUE;
}
// Save configuration
BOOL CConfig::Save()
{
return TRUE;
}
// Return MainBase
LPWSTR CConfig::GetMainBase()
{
return MainBase;
}
// Set MainBase
void CConfig::SetMainBase(LPWSTR lpwsMainBase)
{
MainBase = lpwsMainBase;
}
// Return HttpBase
LPSTR CConfig::GetHttpBase()
{
return HttpBase;
}
// Set HttpBase
void CConfig::SetHttpBase(LPSTR lpsHttpBase)
{
HttpBase = lpsHttpBase;
}
// Return DefaultResources
CList<LPSTR>* CConfig::GetDefaultResources()
{
return &DefaultResources;
}
// Return bound port
USHORT CConfig::GetPort()
{
return Port;
}
// Set port
VOID CConfig::SetPort(USHORT wPort)
{
Port = wPort;
}

View file

@ -0,0 +1,16 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: error.cpp
* PURPOSE: Error reporting
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/09/2000 Created
*/
#include <error.h>
#include <stdio.h>
void ReportErrorStr(LPTSTR lpsText)
{
wprintf((__wchar_t*)lpsText);
}

View file

@ -0,0 +1,380 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: http.cpp
* PURPOSE: HTTP 1.1 parser engine
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/09/2000 Created
* TODO: - Implement message-body
* - Implement more generel-header entries
* - Implement more request-header entries
* - Implement more entity-header entries
*/
#include <debug.h>
#include <iostream.h>
#include <string.h>
#include <http.h>
CHAR MethodTable[NUMMETHODS][8] = {"OPTIONS", "GET", "HEAD", "POST", "PUT",
"DELETE", "TRACE"};
CHAR GenerelTable[NUMGENERELS][18] = {"Cache-Control", "Connection", "Date", "Pragma",
"Transfer-Encoding", "Upgrade", "Via"};
CHAR RequestTable[NUMREQUESTS][20] = {"Accept", "Accept-Charset", "Accept-Encoding",
"Accept-Language", "Authorization", "From", "Host", "If-Modified-Since", "If-Match",
"If-None-Match", "If-Range", "If-Unmodified-Since", "Max-Forwards",
"Proxy-Authorization", "Range", "Referer", "User-Agent"};
CHAR EntityTable[NUMENTITIES][17] = {"Allow", "Content-Base", "Content-Encoding",
"Content-Language", "Content-Length", "Content-Location", "Content-MD5",
"Content-Range", "Content-Type", "ETag", "Expires", "Last-Modified"};
// *************************** CHttpParser ***************************
// Default constructor
CHttpParser::CHttpParser()
{
nHead = 0;
nTail = 0;
}
// Default destructor
CHttpParser::~CHttpParser()
{
}
// Returns TRUE if a complete HTTP message is in buffer
BOOL CHttpParser::Complete()
{
UINT nTmp;
/*DPRINT("--1:-%d---\n", sBuffer[nHead-2]);
DPRINT("--2:-%d---\n", sBuffer[nHead-1]);
sBuffer[nHead] = '!';
sBuffer[nHead+1] = 0;
DPRINT("Examining buffer: (Head: %d, Tail: %d)\n", nHead, nTail);
DPRINT("%s\n", (LPSTR)&sBuffer[nTail]);*/
nTmp = nTail;
if (!Parse()) {
if (!bUnknownMethod)
nTail = nTmp;
return FALSE;
} else
return TRUE;
}
// Read a character from buffer
BOOL CHttpParser::ReadChar(LPSTR lpsStr)
{
if (nTail <= nHead) {
if (nTail != nHead) {
lpsStr[0] = sBuffer[nTail];
nTail++;
return TRUE;
} else {
lpsStr[0] = 0;
return FALSE;
}
} else {
if (nTail == sizeof(sBuffer))
nTail = 0;
if (nTail != nHead) {
lpsStr[0] = sBuffer[nTail];
nTail++;
return TRUE;
} else {
lpsStr[0] = 0;
return FALSE;
}
}
}
// Peek at a character in the buffer
BOOL CHttpParser::PeekChar(LPSTR lpsStr)
{
UINT nFakeTail;
if (nTail == sizeof(sBuffer))
nFakeTail = 0;
else
nFakeTail = nTail;
if (nFakeTail != nHead) {
lpsStr[0] = sBuffer[nFakeTail];
return TRUE;
} else {
lpsStr[0] = 0;
return FALSE;
}
}
// Read a string from buffer. Only A-Z, a-z, 0-9 and '-' are valid characters
BOOL CHttpParser::ReadString(LPSTR lpsStr, UINT nLength)
{
UINT i = 0;
CHAR sTmp;
while (PeekChar(&sTmp)) {
if (((sTmp >= 'A') && (sTmp <= 'Z')) || ((sTmp >= 'a') && (sTmp <= 'z')) ||
((sTmp >= '0') && (sTmp <= '9')) || (sTmp == '-')) {
if (i >= (nLength - 1)) {
lpsStr[0] = 0;
return FALSE;
}
ReadChar(&sTmp);
lpsStr[i] = sTmp;
i++;
} else {
lpsStr[i] = 0;
return TRUE;
}
}
lpsStr[0] = 0;
return FALSE;
}
// Read a string from buffer. Stop if SP or CR is found or when there are no more
// characters
BOOL CHttpParser::ReadSpecial(LPSTR lpsStr, UINT nLength)
{
UINT i = 0;
CHAR sTmp;
while (PeekChar(&sTmp) && (sTmp != ' ') && (sTmp != 13)) {
if (i >= (nLength - 1)) {
lpsStr[nLength - 1] = 0;
return FALSE;
}
ReadChar(&sTmp);
lpsStr[i] = sTmp;
i++;
}
lpsStr[i] = 0;
return TRUE;
}
// Skip until "sCh" is found
VOID CHttpParser::Skip(CHAR sCh)
{
CHAR sTmp;
while (PeekChar(&sTmp) && (sTmp != sCh))
ReadChar(&sTmp);
}
// Return TRUE if sCh is the next character
BOOL CHttpParser::Expect(CHAR sCh)
{
CHAR sTmp;
if (PeekChar(&sTmp)) {
if (sTmp == sCh) {
ReadChar(&sTmp);
return TRUE;
}
}
return FALSE;
}
// Return TRUE if CRLF are the next characters
BOOL CHttpParser::ExpectCRLF()
{
return (Expect(13) && Expect(10));
}
// Request = RequestLine | *( GenerelHeader | RequestHeader | EntityHeader )
// CRLF [ MessageBody ]
BOOL CHttpParser::Parse()
{
BOOL bStatus;
CHAR ch;
if (RequestLine()) {
do {
if (!ReadString(sHeader, sizeof(sHeader)))
break;
bStatus = (GenerelHeader());
bStatus = (RequestHeader() || bStatus);
bStatus = (EntityHeader() || bStatus);
} while (bStatus);
// CRLF
if (!ExpectCRLF())
return FALSE;
MessageBody();
return TRUE;
}
return FALSE;
}
// RequestLine = Method SP RequestURI SP HTTP-Version CRLF
BOOL CHttpParser::RequestLine()
{
CHAR sCh;
UINT i;
bUnknownMethod = FALSE;
// RFC 2068 states that servers SHOULD ignore any empty nine(s) received where a
// Request-Line is expected
while (PeekChar(&sCh) && ((sCh == 13) || (sCh == 10)));
if (!ReadString(sMethod, sizeof(sMethod)))
return FALSE;
for (i = 0; i < NUMMETHODS; i++) {
if (strcmp(MethodTable[i], sMethod) == 0) {
nMethodNo = i;
if (!Expect(' '))
return FALSE;
// URI (ie. host/directory/resource)
if (!ReadSpecial(sUri, sizeof(sUri)))
return FALSE;
if (!Expect(' '))
return FALSE;
// HTTP version (eg. HTTP/1.1)
if (!ReadSpecial(sVersion, sizeof(sVersion)))
return FALSE;
// CRLF
if (!ExpectCRLF())
return FALSE;
return TRUE;
}
}
bUnknownMethod = TRUE;
return FALSE;
}
// GenerelHeader = Cache-Control | Connection | Date | Pragma | Transfer-Encoding |
// Upgrade | Via
BOOL CHttpParser::GenerelHeader()
{
INT i;
for (i = 0; i < NUMGENERELS; i++) {
if (strcmp(GenerelTable[i], sHeader) == 0) {
switch (i) {
case 1: {
//Connection
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
default: {
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
}
}
return TRUE;
}
}
return FALSE;
}
// RequestHeader = Accept | Accept-Charset | Accept-Encoding | Accept-Language |
// Authorization | From | Host | If-Modified-Since | If-Match |
// If-None-Match | If-Range | If-Unmodified-Since | Max-Forwards |
// Proxy-Authorization | Range | Referer | User-Agent
BOOL CHttpParser::RequestHeader()
{
INT i;
for (i = 0; i < NUMREQUESTS; i++) {
if (strcmp(RequestTable[i], sHeader) == 0) {
switch (i) {
case 0: {
//Accept
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
case 2: {
//Accept-Encoding
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
case 3: {
//Accept-Language
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
case 6: {
//Host
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
case 16: {
//User-Agent
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
default: {
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
return TRUE;
}
}
return TRUE;
}
}
return FALSE;
}
// EntityHeader = Allow | Content-Base | Content-Encoding | Content-Language |
// Content-Length | Content-Location | Content-MD5 |
// Content-Range | Content-Type | ETag | Expires |
// Last-Modified | extension-header
BOOL CHttpParser::EntityHeader()
{
INT i;
for (i = 0; i < NUMENTITIES; i++) {
if (strcmp(EntityTable[i], sHeader) == 0) {
switch (i) {
case 0:
default: {
//cout << "<Entity-Header>: #" << i << endl;
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
return TRUE;
}
}
return FALSE;
}
}
return FALSE;
}
// MessageBody = *OCTET
BOOL CHttpParser::MessageBody()
{
return FALSE;
}

View file

@ -0,0 +1,491 @@
/*
* 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 <debug.h>
#include <new>
#include <malloc.h>
#include <string.h>
#include <config.h>
#include <httpd.h>
#include <error.h>
using namespace std;
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";
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";
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";
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";
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";
// *************************** 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<LPSTR> *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;
struct {
DWORD Low;
DWORD High;
};
} 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.Low = GetFileSize(hFile, &nTotalBytes.High);
if ((nTotalBytes.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.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(LPSTR 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(_T("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."));
}
}

View file

@ -0,0 +1,49 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: include/config.h
*/
#ifndef __CONFIG_H
#define __CONFIG_H
#include <list.h>
#include <httpd.h>
// General constants
#define APP_DESCRIPTION _T("ReactOS HTTP Daemon")
// Default configuration
#define dcfgDescription _T("Default configuration")
#define dcfgMainBase _T("C:\\roshttpd\\")
#define dcfgHttpBase "C:\\roshttpd\\HttpBase\\"
#define dcfgDefaultResource "index.html"
#define dcfgDefaultPort 80
class CConfig {
public:
CConfig();
~CConfig();
VOID Default();
VOID Clear();
BOOL Load();
BOOL Save();
LPWSTR GetMainBase();
VOID SetMainBase(LPWSTR lpwsMainBase);
LPSTR GetHttpBase();
VOID SetHttpBase(LPSTR lpsHttpBase);
CList<LPSTR>* GetDefaultResources();
USHORT GetPort();
VOID SetPort(USHORT wPort);
private:
VOID Reset();
LPWSTR MainBase;
LPSTR HttpBase;
CList<LPSTR> DefaultResources;
USHORT Port;
};
typedef CConfig* LPCConfig;
extern LPCConfig pConfiguration;
extern LPCHttpDaemonThread pDaemonThread;
#endif /* __CONFIG_H */

View file

@ -0,0 +1,17 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: include/debug.h
*/
#ifndef __DEBUG_H
#define __DEBUG_H
#include <stdio.h>
#ifdef DBG
#define DPRINT(x...) printf(x)
#else
#define DPRINT(x...)
#endif
#endif /* __DEBUG_H */

View file

@ -0,0 +1,15 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: include/error.h
*/
#ifndef __ERROR_H
#define __ERROR_H
#include <windows.h>
#define TS(x) (LPTSTR)_T(x)
void ReportErrorStr(LPTSTR lpsText);
#endif /* __ERROR_H */

View file

@ -0,0 +1,57 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: include/http.h
*/
#ifndef __HTTP_H
#define __HTTP_H
#include <windows.h>
// Generel HTTP related constants
#define NUMMETHODS 7
#define NUMGENERELS 7
#define NUMREQUESTS 17
#define NUMENTITIES 12
// HTTP method constants
#define hmOPTIONS 0
#define hmGET 1
#define hmHEAD 2
#define hmPOST 3
#define hmPUT 4
#define hmDELETE 5
#define hmTRACE 6
class CHttpParser {
public:
CHAR sBuffer[2048];
UINT nHead;
UINT nTail;
CHAR sUri[255];
CHAR sVersion[15];
CHAR sHeader[63];
CHAR sMethod[63];
UINT nMethodNo;
BOOL bUnknownMethod;
BOOL bBadRequest;
CHttpParser();
~CHttpParser();
BOOL Complete();
BOOL Parse();
private:
BOOL ReadChar(LPSTR lpsStr);
BOOL PeekChar(LPSTR lpsStr);
BOOL ReadString(LPSTR lpsStr, UINT nLength);
BOOL ReadSpecial(LPSTR lpStr, UINT nLength);
VOID Skip(CHAR sStr);
BOOL Expect(CHAR sStr);
BOOL ExpectCRLF();
BOOL RequestLine();
BOOL GenerelHeader();
BOOL RequestHeader();
BOOL EntityHeader();
BOOL MessageBody();
};
#endif /* __HTTP_H */

View file

@ -0,0 +1,83 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: include/httpd.h
*/
#ifndef __HTTPD_H
#define __HTTPD_H
#include <thread.h>
#include <socket.h>
#include <http.h>
#define HTTPD_START WM_USER + 1
#define HTTPD_STOP WM_USER + 2
#define HTTPD_SUSPEND WM_USER + 3
#define HTTPD_RESUME WM_USER + 4
enum HTTPdState {
hsStopped = 0,
hsRunning,
hsSuspended
};
class CHttpDaemon;
class CHttpClient : public CServerClientSocket {
public:
CHttpClient();
CHttpClient(LPCServerSocket lpServerSocket);
virtual void OnRead();
//virtual void OnWrite();
virtual void OnClose();
HANDLE ThreadHandle;
DWORD ThreadId;
CHttpParser Parser;
void SplitUri(const LPSTR lpsUri, LPSTR lpsHost, LPSTR lpsResource, LPSTR lpsParams);
void SplitResource(const LPSTR lpsResource, LPSTR lpsPath, LPSTR lpsFilename, LPSTR lpsExtension);
void ProcessRequest();
void SendFile(const LPSTR lpsFilename);
void Report(const LPSTR lpsCode, const LPSTR lpsStr);
private:
BOOL bStop;
LPSTR lpsBuffer;
LONG nBufferSize;
unsigned __int64 nTotalRead;
unsigned __int64 nFileSize;
HANDLE hFile;
};
typedef CHttpClient* LPCHttpClient;
class CHttpClientThread : public CServerClientThread {
public:
CHttpClientThread() {};
CHttpClientThread(LPCServerClientSocket Socket);
virtual void Execute();
};
typedef CHttpClientThread* LPCHttpClientThread;
class CHttpDaemon : public CServerSocket {
public:
CHttpDaemon();
virtual ~CHttpDaemon();
HTTPdState GetState() const;
virtual BOOL Start();
virtual BOOL Stop();
virtual LPCServerClientSocket OnGetSocket(LPCServerSocket lpServerSocket);
virtual LPCServerClientThread OnGetThread(LPCServerClientSocket Socket);
virtual void OnAccept(const LPCServerClientThread lpThread);
private:
HTTPdState State;
};
typedef CHttpDaemon* LPCHttpDaemon;
class CHttpDaemonThread : public CThread {
public:
CHttpDaemonThread() {};
virtual void Execute();
private:
CHttpDaemon *Daemon;
};
typedef CHttpDaemonThread* LPCHttpDaemonThread;
#endif /* __HTTPD_H */

View file

@ -0,0 +1,20 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: include/iterator.h
*/
#ifndef __ITERATOR_H
#define __ITERATOR_H
#include <windows.h>
template <class Item>
class CIterator {
public:
virtual VOID First() = 0;
virtual VOID Next() = 0;
virtual BOOL IsDone() const = 0;
virtual Item CurrentItem() const = 0;
};
#endif /* __ITERATOR_H */

View file

@ -0,0 +1,233 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: include/list.h
*/
#ifndef __LIST_H
#define __LIST_H
#include <windows.h>
#include <iterator.h>
class CListNode {
public:
CListNode();
CListNode(VOID *element, CListNode *next, CListNode *prev);
~CListNode() {};
PVOID operator new(/*size_t s*/ UINT s);
VOID operator delete(PVOID p);
VOID SetElement(PVOID element);
VOID SetNext(CListNode *next);
VOID SetPrev(CListNode *prev);
PVOID GetElement();
CListNode *GetNext();
CListNode *GetPrev();
private:
PVOID Element;
CListNode *Next;
CListNode *Prev;
static HANDLE hHeap;
static INT nRef;
};
template <class Item> class CList {
public:
//CList(CList&);
CList();
~CList();
CList& operator=(CList&);
CIterator<Item> *CreateIterator() const;
LONG Count() const;
Item& Get(const LONG index) const;
// Can throw bad_alloc
VOID Insert(Item& element);
VOID Remove(Item& element);
VOID RemoveAll();
CListNode *GetHeader() const;
CListNode *GetTrailer() const;
private:
CListNode *Search(Item& element) const;
LONG NodeCount;
CListNode *Header;
CListNode *Trailer;
};
template <class Item> class CListIterator : public CIterator<Item> {
public:
CListIterator(const CList<Item> *list);
virtual VOID First();
virtual VOID Next();
virtual BOOL IsDone() const;
virtual Item CurrentItem() const;
private:
const CList<Item> *List;
CListNode *Current;
};
// ****************************** CList ******************************
// Default constructor
template <class Item>
CList<Item>::CList()
{
// Create dummy nodes
Trailer = new CListNode;
Header = new CListNode;
Header->SetNext(Trailer);
Trailer->SetPrev(Header);
}
// Default destructor
template <class Item>
CList<Item>::~CList()
{
RemoveAll();
delete Trailer;
delete Header;
}
// Create an iterator for the list
template <class Item>
CIterator<Item> *CList<Item>::CreateIterator() const
{
return new CListIterator<Item>((CList<Item> *) this);
}
// Return number of elements in list
template <class Item>
LONG CList<Item>::Count() const
{
return NodeCount;
}
// Return element at index
template <class Item>
Item& CList<Item>::Get(const LONG index) const
{
CListNode *node;
if ((index < 0) || (index >= NodeCount))
return NULL;
node = Header;
for (i = 0; i <= index; i++)
node = node->GetNext();
return (Item *) node->GetElement();
}
// Insert an element into the list
template <class Item>
VOID CList<Item>::Insert(Item& element)
{
CListNode *node;
node = new CListNode((PVOID)element, Trailer, Trailer->GetPrev());
Trailer->GetPrev()->SetNext(node);
Trailer->SetPrev(node);
NodeCount++;
}
// Remove an element from the list
template <class Item>
VOID CList<Item>::Remove(Item& element)
{
CListNode *node;
node = Search(element);
if (node != NULL) {
node->GetPrev()->SetNext(node->GetNext());
node->GetNext()->SetPrev(node->GetPrev());
NodeCount--;
delete node;
}
}
// Remove all elements in list
template <class Item>
VOID CList<Item>::RemoveAll()
{
CListNode *node;
CListNode *tmp;
node = Header->GetNext();
while (node != Trailer) {
tmp = node->GetNext();
delete node;
node = tmp;
}
Header->SetNext(Trailer);
Trailer->SetPrev(Header);
NodeCount = 0;
}
// Return header node
template <class Item>
CListNode *CList<Item>::GetHeader() const
{
return Header;
}
// Return trailer node
template <class Item>
CListNode *CList<Item>::GetTrailer() const
{
return Trailer;
}
// Searches for a node that contains the element. Returns NULL if element is not found
template <class Item>
CListNode *CList<Item>::Search(Item& element) const
{
CListNode *node;
node = Header;
while (((node = node->GetNext()) != Trailer) && (node->GetElement() != element));
if (node != Trailer)
return node;
else
return NULL;
}
// ************************** CListIterator **************************
// Default constructor
template <class Item>
CListIterator<Item>::CListIterator(const CList<Item> *list) : List(list)
{
First();
}
// Go to first element in list
template <class Item>
VOID CListIterator<Item>::First()
{
Current = List->GetHeader()->GetNext();
}
// Go to next element in list
template <class Item>
VOID CListIterator<Item>::Next()
{
if (!IsDone())
Current = Current->GetNext();
}
// Return FALSE when there are more elements in list and TRUE when there are no more
template <class Item>
BOOL CListIterator<Item>::IsDone() const
{
return (Current == List->GetTrailer());
}
// Return current element
template <class Item>
Item CListIterator<Item>::CurrentItem() const
{
return IsDone()? NULL : (Item) Current->GetElement();
}
#endif /* __LIST_H */

View file

@ -0,0 +1,141 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: include/socket.h
*/
#ifndef __SOCKET_H
#define __SOCKET_H
#include <crtdll/stdio.h>
#include <windows.h>
#include <winsock2.h>
#include <thread.h>
#include <list.h>
#include <exception>
#include <assert.h>
#define MAX_PENDING_CONNECTS 4 // The backlog allowed for listen()
VOID InitWinsock();
VOID DeinitWinsock();
class CSocket;
class CClientSocket;
class CServerClientSocket;
class CServerClientThread;
class CServerSocket;
typedef CSocket* LPCSocket;
typedef CClientSocket* LPCClientSocket;
typedef CServerClientSocket* LPCServerClientSocket;
typedef CServerClientThread* LPCServerClientThread;
typedef CServerSocket* LPCServerSocket;
class ESocket {
public:
ESocket() { Description = NULL; }
ESocket(LPTSTR description) { Description = description; }
LPTSTR what() { return Description; }
protected:
LPTSTR Description;
};
class ESocketWinsock : public ESocket {
public:
ESocketWinsock(LPTSTR description) { Description = description; }
};
class ESocketDll : public ESocket {
public:
ESocketDll(LPTSTR description) { Description = description; }
};
class ESocketOpen : public ESocket {
public:
ESocketOpen(LPTSTR description) { Description = description; }
};
class ESocketClose : public ESocket {
public:
ESocketClose(LPTSTR description) { Description = description; }
};
class ESocketSend : public ESocket {
public:
ESocketSend(LPTSTR description) { Description = description; }
};
class ESocketReceive : public ESocket {
public:
ESocketReceive(LPTSTR description) { Description = description; }
};
class CSocket {
public:
CSocket();
virtual ~CSocket();
virtual SOCKET GetSocket();
virtual VOID SetSocket(SOCKET socket);
virtual SOCKADDR_IN GetSockAddrIn();
virtual VOID SetSockAddrIn(SOCKADDR_IN sockaddrin);
virtual VOID SetEvents(LONG lEvents);
virtual LONG GetEvents();
virtual VOID SetPort( UINT nPort) {};
virtual VOID Open();
virtual VOID Close();
virtual INT Transmit( LPSTR lpsBuffer, UINT nLength) { return 0; };
virtual INT Receive(LPSTR lpsBuffer, UINT nLength) { return 0; };
virtual INT SendText( LPSTR lpsStr) { return 0; };
protected:
SOCKET Socket;
SOCKADDR_IN SockAddrIn;
WSAEVENT Event;
UINT Port;
BOOL Active;
private:
LONG Events;
};
class CServerClientSocket : public CSocket {
public:
CServerClientSocket() {};
CServerClientSocket(LPCServerSocket lpServerSocket);
CServerSocket *GetServerSocket();
virtual INT Transmit( LPSTR lpsBuffer, UINT nLength);
virtual INT Receive(LPSTR lpsBuffer, UINT nLength);
virtual INT SendText( LPSTR lpsText);
virtual VOID MessageLoop();
virtual VOID OnRead() {};
//virtual VOID OnWrite() {};
virtual VOID OnClose() {};
protected:
LPCServerSocket ServerSocket;
};
class CServerClientThread : public CThread {
public:
CServerClientThread() {};
CServerClientThread(CServerClientSocket *socket);
virtual ~CServerClientThread();
protected:
CServerClientSocket *ClientSocket;
};
class CServerSocket : public CSocket {
public:
CServerSocket();
virtual ~CServerSocket();
virtual VOID SetPort( UINT nPort);
virtual VOID Open();
virtual VOID Close();
virtual LPCServerClientSocket OnGetSocket(LPCServerSocket lpServerSocket);
virtual LPCServerClientThread OnGetThread(LPCServerClientSocket lpSocket);
virtual VOID OnAccept( LPCServerClientThread lpThread) {};
virtual VOID MessageLoop();
VOID InsertClient(LPCServerClientThread lpClient);
VOID RemoveClient(LPCServerClientThread lpClient);
protected:
CList<LPCServerClientThread> Connections;
};
#endif /* __SOCKET_H */

View file

@ -0,0 +1,34 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: include/thread.h
*/
#ifndef __THREAD_H
#define __THREAD_H
#include <windows.h>
class CThread;
struct ThreadData {
CThread *ClassPtr;
HANDLE hFinished;
};
class CThread {
public:
CThread();
virtual ~CThread();
BOOL PostMessage(UINT Msg, WPARAM wParam, LPARAM lParam);
virtual void Execute();
virtual void Terminate();
BOOL Terminated();
protected:
BOOL bTerminated;
DWORD dwThreadId;
HANDLE hThread;
ThreadData Data;
};
typedef CThread *LPCThread;
#endif /* __THREAD_H */

View file

@ -0,0 +1,54 @@
#
# ReactOS HTTP Daemon
#
PATH_TO_TOP = ../../..
TARGETNAME=roshttpd
CFLAGS = -Iinclude -DUNICODE -D_UNICODE -DDBG
MAIN_OBJECTS = $(TARGETNAME).o config.o error.o http.o httpd.o
COMMON_OBJECTS = common/list.o common/socket.o common/thread.o
OBJECTS = $(MAIN_OBJECTS) $(COMMON_OBJECTS)
PROGS = $(TARGETNAME).exe
LIBS = ../../../lib/kernel32/kernel32.a \
../../../lib/ws2_32/ws2_32.a
ifeq ($(DOSCLI), yes)
CLEAN_FILES = *.o $(TARGETNAME).exe $(TARGETNAME).sym common\*.o
else
CLEAN_FILES = *.o $(TARGETNAME).exe $(TARGETNAME).sym common/*.o
endif
all: $(TARGETNAME).exe
clean: $(CLEAN_FILES:%=%_clean)
$(CLEAN_FILES:%=%_clean): %_clean:
- $(RM) $*
.phony: clean $(CLEAN_FILES:%=%_clean)
install: $(PROGS:%=$(FLOPPY_DIR)/apps/%)
$(PROGS:%=$(FLOPPY_DIR)/apps/%): $(FLOPPY_DIR)/apps/%: %
ifeq ($(DOSCLI),yes)
$(CP) $* $(FLOPPY_DIR)\apps\$*
else
$(CP) $* $(FLOPPY_DIR)/apps/$*
endif
dist: $(PROGS:%=../../$(DIST_DIR)/apps/%)
$(PROGS:%=../../$(DIST_DIR)/apps/%): ../../$(DIST_DIR)/apps/%: %
ifeq ($(DOSCLI),yes)
$(CP) $* ..\..\$(DIST_DIR)\apps\$*
else
$(CP) $* ../../$(DIST_DIR)/apps/$*
endif
$(TARGETNAME).exe: $(OBJECTS) $(LIBS)
$(CC) $(OBJECTS) $(LIBS) -o $(TARGETNAME).exe
include ../../../rules.mak

View file

@ -0,0 +1,66 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: roshttpd.cpp
* PURPOSE: Main program
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/09/2000 Created
*/
#include <debug.h>
#include <new>
#include <winsock2.h>
#include <stdio.h>
#include <config.h>
#include <error.h>
#include <httpd.h>
using namespace std;
VOID Run()
{
InitWinsock();
pDaemonThread = NULL;
pConfiguration = NULL;
try {
// Create configuration object
pConfiguration = new CConfig;
pConfiguration->Default();
// Create daemon object
pDaemonThread = new CHttpDaemonThread;
MSG Msg;
BOOL bQuit = FALSE;
while ((!bQuit) && (!pDaemonThread->Terminated())) {
bQuit = PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE);
if (!bQuit)
DispatchMessage(&Msg);
}
delete pDaemonThread;
if (pConfiguration != NULL)
delete pConfiguration;
} catch (bad_alloc e) {
if (pConfiguration != NULL)
delete pConfiguration;
ReportErrorStr(TS("Insufficient resources."));
}
DeinitWinsock();
}
/* Program entry point */
int main(int argc, char* argv[])
{
printf("ReactOS HTTP Daemon\n");
printf("Type Control-C to stop.\n");
Run();
printf("Daemon stopped.\n");
}