📄 httpsrvr.cxx
字号:
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 {
const char * text;
int code;
BOOL allowedBody;
int majorVersion;
int minorVersion;
};
static const httpStatusCodeStruct * GetStatusCodeStruct(int code)
{
static const httpStatusCodeStruct httpStatusDefn[] = {
// First entry MUST be InternalServerError
{ "Internal Server Error", PHTTP::InternalServerError, 1 },
{ "OK", PHTTP::OK, 1 },
{ "Unauthorised", PHTTP::UnAuthorised, 1 },
{ "Forbidden", PHTTP::Forbidden, 1 },
{ "Not Found", PHTTP::NotFound, 1 },
{ "Not Modified", PHTTP::NotModified },
{ "No Content", PHTTP::NoContent },
{ "Bad Gateway", PHTTP::BadGateway, 1 },
{ "Bad Request", PHTTP::BadRequest, 1 },
{ "Continue", PHTTP::Continue, 1, 1, 1 },
{ "Switching Protocols", PHTTP::SwitchingProtocols, 1, 1, 1 },
{ "Created", PHTTP::Created, 1 },
{ "Accepted", PHTTP::Accepted, 1 },
{ "Non-Authoritative Information", PHTTP::NonAuthoritativeInformation, 1, 1, 1 },
{ "Reset Content", PHTTP::ResetContent, 0, 1, 1 },
{ "Partial Content", PHTTP::PartialContent, 1, 1, 1 },
{ "Multiple Choices", PHTTP::MultipleChoices, 1, 1, 1 },
{ "Moved Permanently", PHTTP::MovedPermanently, 1 },
{ "Moved Temporarily", PHTTP::MovedTemporarily, 1 },
{ "See Other", PHTTP::SeeOther, 1, 1, 1 },
{ "Use Proxy", PHTTP::UseProxy, 1, 1, 1 },
{ "Payment Required", PHTTP::PaymentRequired, 1, 1, 1 },
{ "Method Not Allowed", PHTTP::MethodNotAllowed, 1, 1, 1 },
{ "None Acceptable", PHTTP::NoneAcceptable, 1, 1, 1 },
{ "Proxy Authetication Required", PHTTP::ProxyAuthenticationRequired, 1, 1, 1 },
{ "Request Timeout", PHTTP::RequestTimeout, 1, 1, 1 },
{ "Conflict", PHTTP::Conflict, 1, 1, 1 },
{ "Gone", PHTTP::Gone, 1, 1, 1 },
{ "Length Required", PHTTP::LengthRequired, 1, 1, 1 },
{ "Unless True", PHTTP::UnlessTrue, 1, 1, 1 },
{ "Not Implemented", PHTTP::NotImplemented, 1 },
{ "Service Unavailable", PHTTP::ServiceUnavailable, 1, 1, 1 },
{ "Gateway Timeout", PHTTP::GatewayTimeout, 1, 1, 1 }
};
// make sure the error code is valid
for (PINDEX i = 0; i < PARRAYSIZE(httpStatusDefn); i++)
if (code == httpStatusDefn[i].code)
return &httpStatusDefn[i];
return httpStatusDefn;
}
void PHTTPServer::StartResponse(StatusCode code,
PMIMEInfo & headers,
long bodySize)
{
if (connectInfo.majorVersion < 1)
return;
httpStatusCodeStruct dummyInfo;
const httpStatusCodeStruct * statusInfo;
if (connectInfo.commandCode < NumCommands)
statusInfo = GetStatusCodeStruct(code);
else {
dummyInfo.text = "";
dummyInfo.code = code;
dummyInfo.allowedBody = TRUE;
dummyInfo.majorVersion = connectInfo.majorVersion;
dummyInfo.minorVersion = connectInfo.minorVersion;
statusInfo = &dummyInfo;
}
// output the command line
*this << "HTTP/" << connectInfo.majorVersion << '.' << connectInfo.minorVersion
<< ' ' << statusInfo->code << ' ' << statusInfo->text << "\r\n";
// output the headers. But don't put in ContentLength if the bodysize is zero
// because that can be confused by some browsers as meaning there is no body length.
if (bodySize > 0 && !headers.Contains(ContentLengthTag))
headers.SetAt(ContentLengthTag, PString(PString::Unsigned, (PINDEX)bodySize));
*this << setfill('\r') << headers;
#ifdef STRANGE_NETSCAPE_BUG
// The following is a work around for a strange bug in Netscape where it
// locks up when a persistent connection is made and data less than 1k
// (including MIME headers) is sent. Go figure....
if (bodySize < 1024 && connectInfo.GetMIME()(UserAgentTag).Find("Mozilla/2.0") != P_MAX_INDEX)
nextTimeout.SetInterval(STRANGE_NETSCAPE_BUG*1000);
#endif
}
void PHTTPServer::SetDefaultMIMEInfo(PMIMEInfo & info,
const PHTTPConnectionInfo & connectInfo)
{
PTime now;
if (!info.Contains(DateTag))
info.SetAt(DateTag, now.AsString(PTime::RFC1123, PTime::GMT));
if (!info.Contains(MIMEVersionTag))
info.SetAt(MIMEVersionTag, "1.0");
if (!info.Contains(ServerTag))
info.SetAt(ServerTag, GetServerName());
if (connectInfo.IsPersistant()) {
if (connectInfo.IsProxyConnection())
//{ PError << "Server: setting proxy persistant response" << endl;
info.SetAt(ProxyConnectionTag, KeepAliveTag);
// }
else
//{ PError << "Server: setting direct persistant response" << endl;
info.SetAt(ConnectionTag, KeepAliveTag);
// }
}
}
BOOL PHTTPServer::OnUnknown(const PCaselessString & cmd,
const PHTTPConnectionInfo & connectInfo)
{
return OnError(NotImplemented, cmd, connectInfo);
}
BOOL PHTTPServer::OnError(StatusCode code,
const PCaselessString & extra,
const PHTTPConnectionInfo & connectInfo)
{
const httpStatusCodeStruct * statusInfo = GetStatusCodeStruct(code);
if (!connectInfo.IsCompatible(statusInfo->majorVersion, statusInfo->minorVersion))
statusInfo = GetStatusCodeStruct((code/100)*100);
PMIMEInfo headers;
SetDefaultMIMEInfo(headers, connectInfo);
if (!statusInfo->allowedBody) {
StartResponse(code, headers, 0);
return statusInfo->code == OK;
}
PString reply;
if (extra.Find("<body") != P_MAX_INDEX)
reply = extra;
else {
PHTML html;
html << PHTML::Title()
<< statusInfo->code
<< ' '
<< statusInfo->text
<< PHTML::Body()
<< PHTML::Heading(1)
<< statusInfo->code
<< ' '
<< statusInfo->text
<< PHTML::Heading(1)
<< extra
<< PHTML::Body();
reply = html;
}
headers.SetAt(ContentTypeTag, "text/html");
StartResponse(code, headers, reply.GetLength());
WriteString(reply);
return statusInfo->code == OK;
}
//////////////////////////////////////////////////////////////////////////////
// PHTTPSimpleAuth
void PHTTPAuthority::DecodeBasicAuthority(const PString & authInfo,
PString & username,
PString & password)
{
PString decoded;
if (authInfo(0, 5) *= "Basic ")
decoded = PBase64::Decode(authInfo(6, P_MAX_INDEX));
else
decoded = PBase64::Decode(authInfo);
PINDEX colonPos = decoded.Find(':');
if (colonPos == P_MAX_INDEX) {
username = decoded;
password = PString();
}
else {
username = decoded.Left(colonPos).Trim();
password = decoded.Mid(colonPos+1).Trim();
}
}
BOOL PHTTPAuthority::IsActive() const
{
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
// PHTTPSimpleAuth
PHTTPSimpleAuth::PHTTPSimpleAuth(const PString & realm_,
const PString & username_,
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
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -