📄 httpsrvr.cxx
字号:
info = new PMultipartFormInfo; // read MIME information info->mime.ReadFrom(data); // get the content type PString type = info->mime(PHTTP::ContentTypeTag); // check the encoding PString encoding = info->mime("Content-Transfer-Encoding"); // accumulate text until another seperator or end of data PString & buf = info->body; PINDEX len = 0; buf.SetSize(len+1); buf[0] = '\0'; PINDEX sepLen = sep.GetLength(); const char * sepPtr = (const char *)sep; while (data.good()) { buf.SetSize(len); data >> buf[len++]; if ((len >= sepLen) && (memcmp(((const char *)buf) + len - sepLen, sepPtr, sepLen) == 0)) { char ch; data >> ch; if (ch != 0x0d) data.putback(ch); else { data >> ch; if (ch != 0x0a) data.putback(ch); } len -= sepLen; break; } } buf.SetSize(len+1); buf[len] = '\0'; /* while (data.good()) { data >> line; if (line.Find(sep) == 0) break; info->body += line + "\n"; } */ multipartFormInfoArray.Append(info); info = NULL; }#endif}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 = connectInfo.GetPersistenceTimeout(); 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; // make sure the form info is reset for each new operation connectInfo.ResetMultipartFormInfo(); // 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 { connectInfo.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 : { // check for multi-part form POSTs PString postType = (connectInfo.GetMIME())(ContentTypeTag); if (postType.Find("multipart/form-data") == 0) connectInfo.DecodeMultipartFormInfo(postType, connectInfo.entityBody); else // if (postType *= "x-www-form-urlencoded) PURL::SplitQueryVars(connectInfo.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()) { unsigned max = connectInfo.GetPersistenceMaximumTransations(); if (max == 0 || transactionCount < max) return TRUE; } PTRACE(5, "HTTPServer\tConnection end: " << connectInfo.IsPersistant()); // 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::RequestOK, 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;}BOOL PHTTPServer::StartResponse(StatusCode code, PMIMEInfo & headers, long bodySize){ if (connectInfo.majorVersion < 1) return FALSE; 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"; BOOL chunked = FALSE; // If do not have user set content length, decide if we should add one if (!headers.Contains(ContentLengthTag)) { if (connectInfo.minorVersion < 1) { // v1.0 client, 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.SetAt(ContentLengthTag, bodySize); } else { // v1.1 or later, see if will use chunked output chunked = bodySize == P_MAX_INDEX; if (chunked) headers.SetAt(TransferEncodingTag, ChunkedTag); else if (bodySize >= 0 && bodySize < P_MAX_INDEX) headers.SetAt(ContentLengthTag, 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 return chunked;}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()) { PTRACE(5, "HTTPServer\tSetting proxy persistant response"); info.SetAt(ProxyConnectionTag, KeepAliveTag); } else { PTRACE(5, "HTTPServer\tSetting direct persistant response"); 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 == RequestOK; } 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 == RequestOK;}//////////////////////////////////////////////////////////////////////////////// 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!");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -