📄 httpsrvr.cxx
字号:
}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 & _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); }}//////////////////////////////////////////////////////////////////////////////// PHTTPConnectionInfoPHTTPConnectionInfo::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));}//////////////////////////////////////////////////////////////////////////////// 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){ // 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)) { if (request.server.StartResponse(request.code, request.outMIME, request.contentSize)) { // Chunked transfer encoding request.outMIME.RemoveAll();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -