📄 httpsrvr.cxx
字号:
/* * httpsrvr.cxx * * HTTP server classes. * * Portable Windows Library * * Copyright (c) 1993-1998 Equivalence Pty. Ltd. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Equivalence Pty. Ltd. * * Portions are Copyright (C) 1993 Free Software Foundation, Inc. * All Rights Reserved. * * Contributor(s): ______________________________________. * * $Log: httpsrvr.cxx,v $ * Revision 1.32 2000/05/02 07:55:22 craigs * Changed static PString to static const char * to avoid "memory leak" * * Revision 1.31 1999/05/13 04:04:04 robertj * Fixed problem of initialised commandName in ConnectionInfo. * * Revision 1.30 1999/05/12 01:40:47 robertj * Fixed "unknown" response codes being passed on when used with an "unknown" command. * * Revision 1.29 1999/05/11 12:23:22 robertj * Fixed search for persistent connection to accept kee-alive on multile MIME fields. * * Revision 1.28 1999/05/04 15:26:01 robertj * Improved HTTP/1.1 compatibility (pass through user commands). * Fixed problems with quicktime installer. * * Revision 1.27 1999/04/24 11:50:11 robertj * Changed HTTP command parser so will work if some idiot puts spaces in a URL. * * Revision 1.26 1999/04/21 01:58:08 robertj * Fixed problem with reading data for request using second form of PHTTPRequestInfo constructor. * * Revision 1.25 1998/11/30 04:51:59 robertj * New directory structure * * Revision 1.24 1998/11/14 01:11:38 robertj * PPC linux GNU compatibility. * * Revision 1.23 1998/10/31 12:49:23 robertj * Added read/write mutex to the HTTP space variable to avoid thread crashes. * * Revision 1.22 1998/10/25 01:02:41 craigs * Added ability to specify per-directory authorisation for PHTTPDirectory * * Revision 1.21 1998/10/13 14:06:23 robertj * Complete rewrite of memory leak detection code. * * Revision 1.20 1998/09/23 06:22:13 robertj * Added open source copyright license. * * Revision 1.19 1998/08/06 00:54:22 robertj * Fixed bug in sending empty files, caused endless wait in Netscape. * * Revision 1.18 1998/06/16 03:32:14 robertj * Propagated persistence and proxy flags in new connection info instances. * * Revision 1.17 1998/04/01 01:55:16 robertj * Fixed bug when serving HTTPFile that has zero bytes in it. * * Revision 1.16 1998/02/03 06:24:10 robertj * Added local address and port to PHTTPRequest. * Fixed bug in default entity length. should be read to EOF. * Fixed OnError() so can detec HTML bosy tag with parameters. * * Revision 1.14 1998/01/26 00:42:19 robertj * Added more information to PHTTPConnectionInfo. * Made SetDefaultMIMEFields in HTTP Server not set those fields if already set. * * Revision 1.13 1997/10/30 10:22:04 robertj * Added multiple user basic authorisation scheme. * * Revision 1.12 1997/10/03 13:39:25 robertj * Fixed race condition on socket close in Select() function. * * Revision 1.12 1997/10/03 13:31:12 craigs * Added ability to access client socket from within HTTP resources * * Revision 1.11 1997/08/04 10:44:36 robertj * Improved receiving of a POST on a non-persistant connection, do not wait for EOF if have CRLF. * * Revision 1.10 1997/07/14 11:47:13 robertj * Added "const" to numerous variables. * * Revision 1.9 1997/07/08 13:10:26 robertj * Fixed bug in HTTP server where standard error text is not sent to remote client. * * Revision 1.8 1997/04/15 14:32:19 robertj * Fixed case problem for HTTP version string. * * Revision 1.7 1997/03/20 13:01:32 robertj * Fixed bug in proxy POST having unexpexted reset of connection. * * Revision 1.6 1997/02/09 04:09:30 robertj * Fixed GCC warning * * Revision 1.5 1997/01/12 04:15:23 robertj * Globalised MIME tag strings. * * Revision 1.4 1996/12/12 09:24:16 robertj * Persistent proxy connection support (work in progress). * * Revision 1.3 1996/11/10 21:09:33 robertj * Removed redundent GetSocket() call. * Added flush of stream after processing request, important on persistent connections. * * Revision 1.2 1996/10/26 03:31:05 robertj * Changed OnError so can pass in full HTML page as parameter. * * Revision 1.1 1996/09/14 13:02:18 robertj * Initial revision * */#include <ptlib.h>#include <ptlib/sockets.h>#include <ptclib/http.h>#include <ctype.h>#define new PNEW// undefine to remove support for persistant connections#define HAS_PERSISTANCE// define to enable work-around for Netscape persistant connection bug// set to lifetime of suspect sockets (in seconds)#define STRANGE_NETSCAPE_BUG 3// maximum lifetime (in seconds) of persistant connections#define MAX_LIFETIME 30// maximum lifetime (in transactions) of persistant connections#define MAX_TRANSACTIONS 10// maximum delay between characters whilst reading a line of text#define READLINE_TIMEOUT 30// filename to use for directory access directivesstatic const char * accessFilename = "_access";//////////////////////////////////////////////////////////////////////////////// PHTTPSpacePHTTPSpace::PHTTPSpace(){ mutex = new PReadWriteMutex; root = new Node(PString(), NULL);}void PHTTPSpace::DestroyContents(){ delete mutex; delete root;}void PHTTPSpace::CloneContents(const PHTTPSpace * c){ mutex = new PReadWriteMutex; root = new Node(*c->root);}void PHTTPSpace::CopyContents(const PHTTPSpace & c){ mutex = c.mutex; root = c.root;}PHTTPSpace::Node::Node(const PString & nam, Node * parentNode) : PString(nam){ parent = parentNode; resource = NULL;}PHTTPSpace::Node::~Node(){ delete resource;}BOOL PHTTPSpace::AddResource(PHTTPResource * res, AddOptions overwrite){ PAssert(res != NULL, PInvalidParameter); const PStringArray & path = res->GetURL().GetPath(); Node * node = root; for (PINDEX i = 0; i < path.GetSize(); i++) { if (path[i].IsEmpty()) break; if (node->resource != NULL) return FALSE; // Already a resource in tree in partial path PINDEX pos = node->children.GetValuesIndex(path[i]); if (pos == P_MAX_INDEX) pos = node->children.Append(new Node(path[i], node)); node = &node->children[pos]; } if (!node->children.IsEmpty()) return FALSE; // Already a resource in tree further down path. if (overwrite == ErrorOnExist && node->resource != NULL) return FALSE; // Already a resource in tree at leaf delete node->resource; node->resource = res; return TRUE;}BOOL PHTTPSpace::DelResource(const PURL & url){ const PStringArray & path = url.GetPath(); Node * node = root; for (PINDEX i = 0; i < path.GetSize(); i++) { if (path[i].IsEmpty()) break; PINDEX pos = node->children.GetValuesIndex(path[i]); if (pos == P_MAX_INDEX) return FALSE; node = &node->children[pos]; if (node->resource != NULL) return FALSE; } if (!node->children.IsEmpty()) return FALSE; // Still a resource in tree further down path. do { Node * par = node->parent; par->children.Remove(node); node = par; } while (node != NULL && node->children.IsEmpty()); return TRUE;}static const char * const HTMLIndexFiles[] = { "Welcome.html", "welcome.html", "index.html", "Welcome.htm", "welcome.htm", "index.htm"};PHTTPResource * PHTTPSpace::FindResource(const PURL & url){ const PStringArray & path = url.GetPath(); Node * node = root; PINDEX i; for (i = 0; i < path.GetSize(); i++) { if (path[i].IsEmpty()) break; PINDEX pos = node->children.GetValuesIndex(path[i]); if (pos == P_MAX_INDEX) return NULL; node = &node->children[pos]; if (node->resource != NULL) return node->resource; } for (i = 0; i < PARRAYSIZE(HTMLIndexFiles); i++) { PINDEX pos = node->children.GetValuesIndex(PString(HTMLIndexFiles[i])); if (pos != P_MAX_INDEX) return node->children[pos].resource; } return NULL;}//////////////////////////////////////////////////////////////////////////////// PHTTPServerPHTTPServer::PHTTPServer(){ Construct();}PHTTPServer::PHTTPServer(const PHTTPSpace & space) : urlSpace(space){ Construct();}void PHTTPServer::Construct(){ transactionCount = 0; SetReadLineTimeout(PTimeInterval(0, READLINE_TIMEOUT));}BOOL PHTTPServer::ProcessCommand(){ PString args; PINDEX cmd; // if this is not the first command received by this socket, then set // the read timeout appropriately. if (transactionCount > 0) SetReadTimeout(nextTimeout); // this will only return false upon timeout or completely invalid command if (!ReadCommand(cmd, args)) return FALSE; connectInfo.commandCode = (Commands)cmd; if (cmd < NumCommands) connectInfo.commandName = commandNames[cmd]; else { PINDEX spacePos = args.Find(' '); connectInfo.commandName = args.Left(spacePos); args = args.Mid(spacePos); } // if no tokens, error if (args.IsEmpty()) { OnError(BadRequest, args, connectInfo); return FALSE; } if (!connectInfo.Initialise(*this, args)) return FALSE; // now that we've decided we did receive a HTTP request, increment the // count of transactions transactionCount++; nextTimeout.SetInterval(MAX_LIFETIME*1000); PIPSocket * socket = GetSocket(); WORD myPort = (WORD)(socket != NULL ? socket->GetPort() : 80); // the URL that comes with Connect requests is not quite kosher, so // mangle it into a proper URL and do NOT close the connection. // for all other commands, close the read connection if not persistant if (cmd == CONNECT) connectInfo.url = "https://" + args; else { connectInfo.url = args; if (connectInfo.url.GetPort() == 0) connectInfo.url.SetPort(myPort); } BOOL persist; // If the incoming URL is of a proxy type then call OnProxy() which will // probably just go OnError(). Even if a full URL is provided in the // command we should check to see if it is a local server request and process // it anyway even though we are not a proxy. The usage of GetHostName() // below are to catch every way of specifying the host (name, alias, any of // several IP numbers etc). const PURL & url = connectInfo.GetURL(); if (url.GetScheme() != "http" || (url.GetPort() != 0 && url.GetPort() != myPort) || (!url.GetHostName() && !PIPSocket::IsLocalHost(url.GetHostName()))) persist = OnProxy(connectInfo); else { PString entityBody = ReadEntityBody(); // Handle the local request PStringToString postData; switch (cmd) { case GET : persist = OnGET(url, connectInfo.GetMIME(), connectInfo); break; case HEAD : persist = OnHEAD(url, connectInfo.GetMIME(), connectInfo); break; case POST : PURL::SplitQueryVars(entityBody, postData); persist = OnPOST(url, connectInfo.GetMIME(), postData, connectInfo); break; case P_MAX_INDEX: default: persist = OnUnknown(args, connectInfo); } } flush(); // if the function just indicated that the connection is to persist, // and so did the client, then return TRUE. Note that all of the OnXXXX // routines above must make sure that their return value is FALSE if // if there was no ContentLength field in the response. This ensures that // we always close the socket so the client will get the correct end of file if (persist && connectInfo.IsPersistant() && transactionCount < MAX_TRANSACTIONS) return TRUE;// if (connectInfo.IsPersistant())// PError << "Server: connection persistance end" << endl; // close the output stream now and return FALSE Shutdown(ShutdownWrite); return FALSE;}PString PHTTPServer::ReadEntityBody(){ if (connectInfo.GetMajorVersion() < 1) return PString(); PString entityBody; long contentLength = connectInfo.GetEntityBodyLength(); // a content length of > 0 means read explicit length // a content length of < 0 means read until EOF // a content length of 0 means read nothing int count = 0; if (contentLength > 0) { entityBody = ReadString((PINDEX)contentLength); } else if (contentLength == -2) { ReadLine(entityBody, FALSE); } else if (contentLength < 0) { while (Read(entityBody.GetPointer(count+1000)+count, 1000)) count += GetLastReadCount(); entityBody.SetSize(count+1); } // close the connection, if not persistant if (!connectInfo.IsPersistant()) { PIPSocket * socket = GetSocket(); if (socket != NULL) socket->Shutdown(PIPSocket::ShutdownRead); } return entityBody;}PString PHTTPServer::GetServerName() const{ return "PWLib-HTTP-Server/1.0 PWLib/1.0";}void PHTTPServer::SetURLSpace(const PHTTPSpace & space){ urlSpace = space;}BOOL PHTTPServer::OnGET(const PURL & url, const PMIMEInfo & info, const PHTTPConnectionInfo & connectInfo){ urlSpace.StartRead(); PHTTPResource * resource = urlSpace.FindResource(url); if (resource == NULL) { urlSpace.EndRead(); return OnError(NotFound, url.AsString(), connectInfo); } BOOL retval = resource->OnGET(*this, url, info, connectInfo); urlSpace.EndRead(); return retval;}BOOL PHTTPServer::OnHEAD(const PURL & url, const PMIMEInfo & info, const PHTTPConnectionInfo & connectInfo){ urlSpace.StartRead(); PHTTPResource * resource = urlSpace.FindResource(url); if (resource == NULL) { urlSpace.EndRead(); return OnError(NotFound, url.AsString(), connectInfo); } BOOL retval = resource->OnHEAD(*this, url, info, connectInfo); urlSpace.EndRead(); return retval;}BOOL PHTTPServer::OnPOST(const PURL & url, const PMIMEInfo & info, const PStringToString & data, const PHTTPConnectionInfo & connectInfo){ urlSpace.StartRead(); PHTTPResource * resource = urlSpace.FindResource(url); if (resource == NULL) { urlSpace.EndRead(); return OnError(NotFound, url.AsString(), connectInfo); } BOOL retval = resource->OnPOST(*this, url, info, data, connectInfo); urlSpace.EndRead(); return retval;}BOOL PHTTPServer::OnProxy(const PHTTPConnectionInfo & connectInfo){ return OnError(BadGateway, "Proxy not implemented.", connectInfo) && connectInfo.GetCommandCode() != CONNECT;}struct httpStatusCodeStruct {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -