📄 httpsrvr.cxx
字号:
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;}//////////////////////////////////////////////////////////////////////////////// PHTTPSimpleAuthvoid 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;}//////////////////////////////////////////////////////////////////////////////// PHTTPSimpleAuthPHTTPSimpleAuth::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;}//////////////////////////////////////////////////////////////////////////////// PHTTPMultiSimpAuthPHTTPMultiSimpAuth::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);}//////////////////////////////////////////////////////////////////////////////// PHTTPRequestPHTTPRequest::PHTTPRequest(const PURL & u, const PMIMEInfo & iM, PHTTPServer & server) : url(u), inMIME(iM), origin(0), localAddr(0), localPort(0){ code = PHTTP::OK; contentSize = 0; PIPSocket * socket = server.GetSocket(); if (socket != NULL) { socket->GetPeerAddress(origin); socket->GetLocalAddress(localAddr, localPort); }}//////////////////////////////////////////////////////////////////////////////// PHTTPConnectionInfoPHTTPConnectionInfo::PHTTPConnectionInfo(){ commandCode = PHTTP::NumCommands; majorVersion = 0; minorVersion = 9; isPersistant = FALSE; isProxyConnection = FALSE; entityBodyLength = -1;}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; isPersistant = FALSE;#ifndef HAS_PERSISTANCE isProxyConnection = FALSE;#else PString str; // check for Proxy-Connection and Connection strings isProxyConnection = mimeInfo.HasKey(PHTTP::ProxyConnectionTag); if (isProxyConnection) str = mimeInfo[PHTTP::ProxyConnectionTag]; else if (mimeInfo.HasKey(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); }#endif// if (connectInfo.IsPersistant()) {// if (connectInfo.IsProxyConnection())// PError << "Server: Persistant proxy connection received" << endl;// else// PError << "Server: Persistant direct connection received" << endl;// } // 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) {// PError << "Server: persistant connection has no content length" << endl; entityBodyLength = 0; mimeInfo.SetAt(PHTTP::ContentLengthTag, "0"); } } return TRUE;}void PHTTPConnectionInfo::SetMIME(const PString & tag, const PString & value){ mimeInfo.MakeUnique(); mimeInfo.SetAt(tag, value);}void PHTTPConnectionInfo::SetPersistance(BOOL newPersist){#ifdef HAS_PERSISTANCE isPersistant = newPersist;#else isPersistant = FALSE;#endif}BOOL PHTTPConnectionInfo::IsCompatible(int major, int minor) const{ if (minor == 0 && major == 0) return TRUE; else return (majorVersion > major) || ((majorVersion == major) && (minorVersion >= minor));}//////////////////////////////////////////////////////////////////////////////// PHTTPResourcePHTTPResource::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){ if (isGET && info.Contains(PHTTP::IfModifiedSinceTag) && !IsModifiedSince(PTime(info[PHTTP::IfModifiedSinceTag]))) return server.OnError(PHTTP::NotModified, url.AsString(), connectInfo); PHTTPRequest * request = CreateRequest(url, info, 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -