mirror of
https://github.com/reactos/reactos.git
synced 2024-12-27 01:24:38 +00:00
Small HTTP daemon
svn path=/trunk/; revision=1515
This commit is contained in:
parent
05ccbb2696
commit
52f51dc4b9
18 changed files with 2303 additions and 0 deletions
94
reactos/apps/utils/net/roshttpd/common/list.cpp
Normal file
94
reactos/apps/utils/net/roshttpd/common/list.cpp
Normal 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;
|
||||
}
|
343
reactos/apps/utils/net/roshttpd/common/socket.cpp
Normal file
343
reactos/apps/utils/net/roshttpd/common/socket.cpp
Normal 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."));
|
||||
}
|
80
reactos/apps/utils/net/roshttpd/common/thread.cpp
Normal file
80
reactos/apps/utils/net/roshttpd/common/thread.cpp
Normal 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;
|
||||
}
|
130
reactos/apps/utils/net/roshttpd/config.cpp
Normal file
130
reactos/apps/utils/net/roshttpd/config.cpp
Normal 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;
|
||||
}
|
16
reactos/apps/utils/net/roshttpd/error.cpp
Normal file
16
reactos/apps/utils/net/roshttpd/error.cpp
Normal 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);
|
||||
}
|
380
reactos/apps/utils/net/roshttpd/http.cpp
Normal file
380
reactos/apps/utils/net/roshttpd/http.cpp
Normal 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;
|
||||
}
|
491
reactos/apps/utils/net/roshttpd/httpd.cpp
Normal file
491
reactos/apps/utils/net/roshttpd/httpd.cpp
Normal 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."));
|
||||
}
|
||||
}
|
49
reactos/apps/utils/net/roshttpd/include/config.h
Normal file
49
reactos/apps/utils/net/roshttpd/include/config.h
Normal 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 */
|
17
reactos/apps/utils/net/roshttpd/include/debug.h
Normal file
17
reactos/apps/utils/net/roshttpd/include/debug.h
Normal 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 */
|
15
reactos/apps/utils/net/roshttpd/include/error.h
Normal file
15
reactos/apps/utils/net/roshttpd/include/error.h
Normal 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 */
|
57
reactos/apps/utils/net/roshttpd/include/http.h
Normal file
57
reactos/apps/utils/net/roshttpd/include/http.h
Normal 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 */
|
83
reactos/apps/utils/net/roshttpd/include/httpd.h
Normal file
83
reactos/apps/utils/net/roshttpd/include/httpd.h
Normal 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 */
|
20
reactos/apps/utils/net/roshttpd/include/iterator.h
Normal file
20
reactos/apps/utils/net/roshttpd/include/iterator.h
Normal 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 */
|
233
reactos/apps/utils/net/roshttpd/include/list.h
Normal file
233
reactos/apps/utils/net/roshttpd/include/list.h
Normal 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 */
|
141
reactos/apps/utils/net/roshttpd/include/socket.h
Normal file
141
reactos/apps/utils/net/roshttpd/include/socket.h
Normal 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 */
|
34
reactos/apps/utils/net/roshttpd/include/thread.h
Normal file
34
reactos/apps/utils/net/roshttpd/include/thread.h
Normal 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 */
|
54
reactos/apps/utils/net/roshttpd/makefile
Normal file
54
reactos/apps/utils/net/roshttpd/makefile
Normal 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
|
66
reactos/apps/utils/net/roshttpd/roshttpd.cpp
Normal file
66
reactos/apps/utils/net/roshttpd/roshttpd.cpp
Normal 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");
|
||||
}
|
Loading…
Reference in a new issue