📄 httpsrvr.cxx
字号:
const PString & password_)
: realm(realm_), username(username_), password(password_)
{
PAssert(!realm, "Must have a realm!");
}
PObject * PHTTPSimpleAuth::Clone() const
{
return new PHTTPSimpleAuth(realm, username, password);
}
BOOL PHTTPSimpleAuth::IsActive() const
{
return !username || !password;
}
PString PHTTPSimpleAuth::GetRealm(const PHTTPRequest &) const
{
return realm;
}
BOOL PHTTPSimpleAuth::Validate(const PHTTPRequest &,
const PString & authInfo) const
{
PString user, pass;
DecodeBasicAuthority(authInfo, user, pass);
return username == user && password == pass;
}
//////////////////////////////////////////////////////////////////////////////
// PHTTPMultiSimpAuth
PHTTPMultiSimpAuth::PHTTPMultiSimpAuth(const PString & realm_)
: realm(realm_)
{
PAssert(!realm, "Must have a realm!");
}
PHTTPMultiSimpAuth::PHTTPMultiSimpAuth(const PString & realm_,
const PStringToString & users_)
: realm(realm_), users(users_)
{
PAssert(!realm, "Must have a realm!");
}
PObject * PHTTPMultiSimpAuth::Clone() const
{
return new PHTTPMultiSimpAuth(realm, users);
}
BOOL PHTTPMultiSimpAuth::IsActive() const
{
return !users.IsEmpty();
}
PString PHTTPMultiSimpAuth::GetRealm(const PHTTPRequest &) const
{
return realm;
}
BOOL PHTTPMultiSimpAuth::Validate(const PHTTPRequest &,
const PString & authInfo) const
{
PString user, pass;
DecodeBasicAuthority(authInfo, user, pass);
return users.Contains(user) && users[user] == pass;
}
void PHTTPMultiSimpAuth::AddUser(const PString & username, const PString & password)
{
users.SetAt(username, password);
}
//////////////////////////////////////////////////////////////////////////////
// PHTTPRequest
PHTTPRequest::PHTTPRequest(const PURL & _url,
const PMIMEInfo & _mime,
const PMultipartFormInfoArray & _multipartFormInfo,
PHTTPServer & _server)
: server(_server),
url(_url),
inMIME(_mime),
multipartFormInfo(_multipartFormInfo),
origin(0),
localAddr(0),
localPort(0)
{
code = PHTTP::RequestOK;
contentSize = P_MAX_INDEX;
PIPSocket * socket = server.GetSocket();
if (socket != NULL) {
socket->GetPeerAddress(origin);
socket->GetLocalAddress(localAddr, localPort);
}
}
//////////////////////////////////////////////////////////////////////////////
// PHTTPConnectionInfo
PHTTPConnectionInfo::PHTTPConnectionInfo()
: persistenceTimeout(0, DEFAULT_PERSIST_TIMEOUT) // maximum lifetime (in seconds) of persistant connections
{
// maximum lifetime (in transactions) of persistant connections
persistenceMaximum = DEFAULT_PERSIST_TRANSATIONS;
commandCode = PHTTP::NumCommands;
majorVersion = 0;
minorVersion = 9;
isPersistant = FALSE;
wasPersistant = FALSE;
isProxyConnection = FALSE;
entityBodyLength = -1;
multipartFormInfoArray.AllowDeleteObjects();
}
BOOL PHTTPConnectionInfo::Initialise(PHTTPServer & server, PString & args)
{
// if only one argument, then it must be a version 0.9 simple request
PINDEX lastSpacePos = args.FindLast(' ');
static const PCaselessString httpId = "HTTP/";
if (lastSpacePos == P_MAX_INDEX || httpId != args(lastSpacePos+1, lastSpacePos+5)) {
majorVersion = 0;
minorVersion = 9;
return TRUE;
}
// otherwise, attempt to extract a version number
PCaselessString verStr = args.Mid(lastSpacePos + 6);
PINDEX dotPos = verStr.Find('.');
if (dotPos == 0 || dotPos >= verStr.GetLength()) {
server.OnError(PHTTP::BadRequest, "Malformed version number: " + verStr, *this);
return FALSE;
}
// should actually check if the text contains only digits, but the
// chances of matching everything else and it not being a valid number
// are pretty small, so don't bother
majorVersion = (int)verStr.Left(dotPos).AsInteger();
minorVersion = (int)verStr.Mid(dotPos+1).AsInteger();
args.Delete(lastSpacePos, P_MAX_INDEX);
// build our connection info reading MIME info until an empty line is
// received, or EOF
if (!mimeInfo.Read(server))
return FALSE;
wasPersistant = isPersistant;
isPersistant = FALSE;
PString str;
// check for Proxy-Connection and Connection strings
isProxyConnection = mimeInfo.Contains(PHTTP::ProxyConnectionTag);
if (isProxyConnection)
str = mimeInfo[PHTTP::ProxyConnectionTag];
else if (mimeInfo.Contains(PHTTP::ConnectionTag))
str = mimeInfo[PHTTP::ConnectionTag];
// get any connection options
if (!str) {
PStringArray tokens = str.Tokenise(", \r\n", FALSE);
for (PINDEX z = 0; !isPersistant && z < tokens.GetSize(); z++)
isPersistant = isPersistant || (tokens[z] *= PHTTP::KeepAliveTag);
}
// If the protocol is version 1.0 or greater, there is MIME info, and the
// prescence of a an entity body is signalled by the inclusion of
// Content-Length header. If the protocol is less than version 1.0, then
// there is no entity body!
// if the client specified a persistant connection, then use the
// ContentLength field. If there is no content length field, then
// assume a ContentLength of zero and close the connection.
// The spec actually says to read until end of file in this case,
// but Netscape hangs if this is done.
// If the client didn't specify a persistant connection, then use the
// ContentLength if there is one or read until end of file if there isn't
if (!isPersistant)
entityBodyLength = mimeInfo.GetInteger(PHTTP::ContentLengthTag,
(commandCode == PHTTP::POST) ? -2 : 0);
else {
entityBodyLength = mimeInfo.GetInteger(PHTTP::ContentLengthTag, -1);
if (entityBodyLength < 0) {
PTRACE(5, "HTTPServer\tPersistant connection has no content length");
entityBodyLength = 0;
mimeInfo.SetAt(PHTTP::ContentLengthTag, "0");
}
}
return TRUE;
}
void PHTTPConnectionInfo::SetMIME(const PString & tag, const PString & value)
{
mimeInfo.MakeUnique();
mimeInfo.SetAt(tag, value);
}
BOOL PHTTPConnectionInfo::IsCompatible(int major, int minor) const
{
if (minor == 0 && major == 0)
return TRUE;
else
return (majorVersion > major) ||
((majorVersion == major) && (minorVersion >= minor));
}
//////////////////////////////////////////////////////////////////////////////
// PHTTPResource
PHTTPResource::PHTTPResource(const PURL & url)
: baseURL(url)
{
authority = NULL;
hitCount = 0;
}
PHTTPResource::PHTTPResource(const PURL & url, const PHTTPAuthority & auth)
: baseURL(url)
{
authority = (PHTTPAuthority *)auth.Clone();
hitCount = 0;
}
PHTTPResource::PHTTPResource(const PURL & url, const PString & type)
: baseURL(url), contentType(type)
{
authority = NULL;
hitCount = 0;
}
PHTTPResource::PHTTPResource(const PURL & url,
const PString & type,
const PHTTPAuthority & auth)
: baseURL(url), contentType(type)
{
authority = (PHTTPAuthority *)auth.Clone();
hitCount = 0;
}
PHTTPResource::~PHTTPResource()
{
delete authority;
}
BOOL PHTTPResource::OnGET(PHTTPServer & server,
const PURL & url,
const PMIMEInfo & info,
const PHTTPConnectionInfo & connectInfo)
{
return OnGETOrHEAD(server, url, info, connectInfo, TRUE);
}
BOOL PHTTPResource::OnHEAD(PHTTPServer & server,
const PURL & url,
const PMIMEInfo & info,
const PHTTPConnectionInfo & connectInfo)
{
return OnGETOrHEAD(server, url, info, connectInfo, FALSE);
}
BOOL PHTTPResource::OnGETOrHEAD(PHTTPServer & server,
const PURL & url,
const PMIMEInfo & info,
const PHTTPConnectionInfo & connectInfo,
BOOL isGET)
{
// Nede to split songle if into 2 so the Tornado compiler won't end with
// 'internal compiler error'
if (isGET && info.Contains(PHTTP::IfModifiedSinceTag))
if (!IsModifiedSince(PTime(info[PHTTP::IfModifiedSinceTag])))
return server.OnError(PHTTP::NotModified, url.AsString(), connectInfo);
PHTTPRequest * request = CreateRequest(url,
info,
connectInfo.GetMultipartFormInfo(),
server);
BOOL retVal = TRUE;
if (CheckAuthority(server, *request, connectInfo)) {
retVal = FALSE;
server.SetDefaultMIMEInfo(request->outMIME, connectInfo);
PTime expiryDate;
if (GetExpirationDate(expiryDate))
request->outMIME.SetAt(PHTTP::ExpiresTag,
expiryDate.AsString(PTime::RFC1123, PTime::GMT));
if (!LoadHeaders(*request))
retVal = server.OnError(request->code, url.AsString(), connectInfo);
else if (!isGET)
retVal = request->outMIME.Contains(PHTTP::ContentLengthTag);
else {
hitCount++;
retVal = OnGETData(server, url, connectInfo, *request);
}
}
delete request;
return retVal;
}
BOOL PHTTPResource::OnGETData(PHTTPServer & /*server*/,
const PURL & /*url*/,
const PHTTPConnectionInfo & /*connectInfo*/,
PHTTPRequest & request)
{
SendData(request);
return request.outMIME.Contains(PHTTP::ContentLengthTag) ||
request.outMIME.Contains(PHTTP::TransferEncodingTag);
}
BOOL PHTTPResource::OnPOST(PHTTPServer & server,
const PURL & url,
const PMIMEInfo & info,
const PStringToString & data,
const PHTTPConnectionInfo & connectInfo)
{
PHTTPRequest * request = CreateRequest(url,
info,
connectInfo.GetMultipartFormInfo(),
server);
request->entityBody = connectInfo.GetEntityBody();
BOOL persist = TRUE;
if (CheckAuthority(server, *request, connectInfo)) {
server.SetDefaultMIMEInfo(request->outMIME, connectInfo);
persist = OnPOSTData(*request, data);
if (request->code != PHTTP::RequestOK)
persist = server.OnError(request->code, "", connectInfo) && persist;
}
delete request;
return persist;
}
BOOL PHTTPResource::OnPOSTData(PHTTPRequest & request,
const PStringToString & data)
{
PHTML msg;
BOOL persist = Post(request, data, msg);
if (msg.Is(PHTML::InBody))
msg << PHTML::Body();
if (request.code != PHTTP::RequestOK)
return persist;
if (msg.IsEmpty())
msg << PHTML::Title() << (unsigned)PHTTP::RequestOK << " OK" << PHTML::Body()
<< PHTML::Heading(1) << (unsigned)PHTTP::RequestOK << " OK" << PHTML::Heading(1)
<< PHTML::Body();
request.outMIME.SetAt(PHTTP::ContentTypeTag, "text/html");
PINDEX len = msg.GetLength();
request.server.StartResponse(request.code, request.outMIME, len);
return request.server.Write((const char *)msg, len) && persist;
}
BOOL PHTTPResource::CheckAuthority(PHTTPServer & server,
const PHTTPRequest & request,
const PHTTPConnectionInfo & connectInfo)
{
if (authority == NULL)
return TRUE;
return CheckAuthority(*authority, server, request, connectInfo);
}
BOOL PHTTPResource::CheckAuthority(PHTTPAuthority & authority,
PHTTPServer & server,
const PHTTPRequest & request,
const PHTTPConnectionInfo & connectInfo)
{
if (!authority.IsActive())
return TRUE;
// if this is an authorisation request...
if (request.inMIME.Contains(PHTTP::AuthorizationTag) &&
authority.Validate(request, request.inMIME[PHTTP::AuthorizationTag]))
return TRUE;
// it must be a request for authorisation
PMIMEInfo headers;
server.SetDefaultMIMEInfo(headers, connectInfo);
headers.SetAt(PHTTP::WWWAuthenticateTag,
"Basic realm=\"" + authority.GetRealm(request) + "\"");
headers.SetAt(PHTTP::ContentTypeTag, "text/html");
const httpStatusCodeStruct * statusInfo =
GetStatusCodeStruct(PHTTP::UnAuthorised);
PHTML reply;
reply << PHTML::Title()
<< statusInfo->code
<< ' '
<< statusInfo->text
<< PHTML::Body()
<< PHTML::Heading(1)
<< statusInfo->code
<< ' '
<< statusInfo->text
<< PHTML::Heading(1)
<< "Your request cannot be authorised because it requires authentication."
<< PHTML::Paragraph()
<< "This may be because you entered an incorrect username or password, "
<< "or because your browser is not performing Basic authentication."
<< PHTML::Body();
server.StartResponse(PHTTP::UnAuthorised, headers, reply.GetLength());
server.WriteString(reply);
return FALSE;
}
void PHTTPResource::SetAuthority(const PHTTPAuthority & auth)
{
delete authority;
authority = (PHTTPAuthority *)auth.Clone();
}
void PHTTPResource::ClearAuthority()
{
delete authority;
authority = NULL;
}
BOOL PHTTPResource::IsModifiedSince(const PTime &)
{
return TRUE;
}
BOOL PHTTPResource::GetExpirationDate(PTime &)
{
return FALSE;
}
PHTTPRequest * PHTTPResource::CreateRequest(const PURL & url,
const PMIMEInfo & inMIME,
const PMultipartFormInfoArray & multipartFormInfo,
PHTTPServer & socket)
{
return new PHTTPRequest(url, inMIME, multipartFormInfo, socket);
}
static void WriteChunkedDataToServer(PHTTPServer & server, PCharArray & data)
{
if (data.GetSize() == 0)
return;
server << data.GetSize() << "\r\n";
server.Write(data, data.GetSize());
server << "\r\n";
data.SetSize(0);
}
void PHTTPResource::SendData(PHTTPRequest & request)
{
if (!request.outMIME.Contains(PHTTP::ContentTypeTag) && !contentType)
request.outMIME.SetAt(PHTTP::ContentTypeTag, contentType);
PCharArray data;
if (LoadData(request, data)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -