📄 http.cxx
字号:
// news:string
#define DEFAULT_FTP_PORT 21
#define DEFAULT_TELNET_PORT 23
#define DEFAULT_GOPHER_PORT 70
#define DEFAULT_HTTP_PORT 80
#define DEFAULT_NNTP_PORT 119
#define DEFAULT_WAIS_PORT 210
#define DEFAULT_HTTPS_PORT 443
#define DEFAULT_RTSP_PORT 554
#define DEFAULT_RTSPU_PORT 554
#define DEFAULT_PROSPERO_PORT 1525
#define DEFAULT_H323_PORT 1720
#define DEFAULT_H323RAS_PORT 1719
#define DEFAULT_SIP_PORT 5060
#define DEFINE_LEGACY_URL_SCHEME(schemeName, user, pass, host, def, defhost, query, params, frags, path, rel, port) \
class PURLLegacyScheme_##schemeName : public PURLLegacyScheme \
{ \
public: \
PURLLegacyScheme_##schemeName() \
: PURLLegacyScheme(#schemeName ) \
{ \
hasUsername = user; \
hasPassword = pass; \
hasHostPort = host; \
defaultToUserIfNoAt = def; \
defaultHostToLocal = defhost; \
hasQuery = query; \
hasParameters = params; \
hasFragments = frags; \
hasPath = path; \
relativeImpliesScheme = rel; \
defaultPort = port; \
} \
}; \
static PFactory<PURLScheme>::Worker<PURLLegacyScheme_##schemeName> schemeName##Factory(#schemeName, true); \
DEFINE_LEGACY_URL_SCHEME(http, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, DEFAULT_HTTP_PORT )
DEFINE_LEGACY_URL_SCHEME(file, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, 0)
DEFINE_LEGACY_URL_SCHEME(https, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, DEFAULT_HTTPS_PORT)
DEFINE_LEGACY_URL_SCHEME(gopher, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_GOPHER_PORT)
DEFINE_LEGACY_URL_SCHEME(wais, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_WAIS_PORT)
DEFINE_LEGACY_URL_SCHEME(nntp, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_NNTP_PORT)
DEFINE_LEGACY_URL_SCHEME(prospero, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_PROSPERO_PORT)
DEFINE_LEGACY_URL_SCHEME(rtsp, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_RTSP_PORT)
DEFINE_LEGACY_URL_SCHEME(rtspu, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_RTSPU_PORT)
DEFINE_LEGACY_URL_SCHEME(ftp, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_FTP_PORT)
DEFINE_LEGACY_URL_SCHEME(telnet, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, DEFAULT_TELNET_PORT)
DEFINE_LEGACY_URL_SCHEME(mailto, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 0)
DEFINE_LEGACY_URL_SCHEME(news, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, 0)
DEFINE_LEGACY_URL_SCHEME(h323, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_H323_PORT)
DEFINE_LEGACY_URL_SCHEME(sip, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_SIP_PORT)
DEFINE_LEGACY_URL_SCHEME(tel, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0)
DEFINE_LEGACY_URL_SCHEME(fax, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0)
DEFINE_LEGACY_URL_SCHEME(callto, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0)
PINSTANTIATE_FACTORY(PURLScheme, PString)
#define DEFAULT_SCHEME "http"
#define FILE_SCHEME "file"
//////////////////////////////////////////////////////////////////////////////
// PURL
PURL::PURL()
: //scheme(SchemeTable[DEFAULT_SCHEME].name),
scheme(DEFAULT_SCHEME),
port(0),
portSupplied (FALSE),
relativePath(FALSE)
{
}
PURL::PURL(const char * str, const char * defaultScheme)
{
Parse(str, defaultScheme);
}
PURL::PURL(const PString & str, const char * defaultScheme)
{
Parse(str, defaultScheme);
}
PURL::PURL(const PFilePath & filePath)
: //scheme(SchemeTable[FILE_SCHEME].name),
scheme(FILE_SCHEME),
port(0),
portSupplied (FALSE),
relativePath(FALSE)
{
PStringArray pathArray = filePath.GetDirectory().GetPath();
hostname = pathArray[0];
PINDEX i;
for (i = 1; i < pathArray.GetSize(); i++)
pathArray[i-1] = pathArray[i];
pathArray[i-1] = filePath.GetFileName();
SetPath(pathArray);
}
PObject::Comparison PURL::Compare(const PObject & obj) const
{
PAssert(PIsDescendant(&obj, PURL), PInvalidCast);
return urlString.Compare(((const PURL &)obj).urlString);
}
PINDEX PURL::HashFunction() const
{
return urlString.HashFunction();
}
void PURL::PrintOn(ostream & stream) const
{
stream << urlString;
}
void PURL::ReadFrom(istream & stream)
{
PString s;
stream >> s;
Parse(s);
}
PString PURL::TranslateString(const PString & str, TranslationType type)
{
PString xlat = str;
PString safeChars = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789$-_.!*'(),";
switch (type) {
case LoginTranslation :
safeChars += "+;?&=";
break;
case PathTranslation :
safeChars += "+:@&=";
break;
case QueryTranslation :
safeChars += ":@";
}
PINDEX pos = (PINDEX)-1;
while ((pos += 1+strspn(&xlat[pos+1], safeChars)) < xlat.GetLength())
xlat.Splice(psprintf("%%%02X", (BYTE)xlat[pos]), pos, 1);
if (type == QueryTranslation) {
PINDEX space = (PINDEX)-1;
while ((space = xlat.Find(' ', space+1)) != P_MAX_INDEX)
xlat[space] = '+';
}
return xlat;
}
PString PURL::UntranslateString(const PString & str, TranslationType type)
{
PString xlat = str;
xlat.MakeUnique();
PINDEX pos;
if (type == PURL::QueryTranslation) {
pos = (PINDEX)-1;
while ((pos = xlat.Find('+', pos+1)) != P_MAX_INDEX)
xlat[pos] = ' ';
}
pos = (PINDEX)-1;
while ((pos = xlat.Find('%', pos+1)) != P_MAX_INDEX) {
int digit1 = xlat[pos+1];
int digit2 = xlat[pos+2];
if (isxdigit(digit1) && isxdigit(digit2)) {
xlat[pos] = (char)(
(isdigit(digit2) ? (digit2-'0') : (toupper(digit2)-'A'+10)) +
((isdigit(digit1) ? (digit1-'0') : (toupper(digit1)-'A'+10)) << 4));
xlat.Delete(pos+1, 2);
}
}
return xlat;
}
static void SplitVars(const PString & str, PStringToString & vars, char sep1, char sep2)
{
PINDEX sep1prev = 0;
do {
PINDEX sep1next = str.Find(sep1, sep1prev);
if (sep1next == P_MAX_INDEX)
sep1next--; // Implicit assumption string is not a couple of gigabytes long ...
PINDEX sep2pos = str.Find(sep2, sep1prev);
if (sep2pos > sep1next)
sep2pos = sep1next;
PCaselessString key = PURL::UntranslateString(str(sep1prev, sep2pos-1), PURL::QueryTranslation);
if (!key) {
PString data = PURL::UntranslateString(str(sep2pos+1, sep1next-1), PURL::QueryTranslation);
if (vars.Contains(key))
vars.SetAt(key, vars[key] + ',' + data);
else
vars.SetAt(key, data);
}
sep1prev = sep1next+1;
} while (sep1prev != P_MAX_INDEX);
}
void PURL::SplitQueryVars(const PString & queryStr, PStringToString & queryVars)
{
SplitVars(queryStr, queryVars, '&', '=');
}
BOOL PURL::InternalParse(const char * cstr, const char * defaultScheme)
{
urlString = cstr;
scheme.MakeEmpty();
username.MakeEmpty();
password.MakeEmpty();
hostname.MakeEmpty();
port = 0;
portSupplied = FALSE;
relativePath = FALSE;
pathStr.MakeEmpty();
path.SetSize(0);
paramVars.RemoveAll();
fragment.MakeEmpty();
queryVars.RemoveAll();
// copy the string so we can take bits off it
while (isspace(*cstr))
cstr++;
PString url = cstr;
// Character set as per RFC2396
PINDEX pos = 0;
while (isalnum(url[pos]) || url[pos] == '+' || url[pos] == '-' || url[pos] == '.')
pos++;
PString schemeName;
// get information which tells us how to parse URL for this
// particular scheme
PURLScheme * schemeInfo = NULL;
// Determine if the URL has an explicit scheme
if (url[pos] == ':') {
// get the scheme information, or get the default scheme
schemeInfo = PFactory<PURLScheme>::CreateInstance(url.Left(pos));
if (schemeInfo == NULL && defaultScheme == NULL) {
PFactory<PURLScheme>::KeyList_T keyList = PFactory<PURLScheme>::GetKeyList();
if (keyList.size() != 0)
schemeInfo = PFactory<PURLScheme>::CreateInstance(keyList[0]);
}
if (schemeInfo != NULL)
url.Delete(0, pos+1);
}
// if we could not match a scheme, then use the specified default scheme
if (schemeInfo == NULL && defaultScheme != NULL)
schemeInfo = PFactory<PURLScheme>::CreateInstance(defaultScheme);
// if that still fails, then use the global default scheme
if (schemeInfo == NULL)
schemeInfo = PFactory<PURLScheme>::CreateInstance(DEFAULT_SCHEME);
// if that fails, then there is nowehere to go
PAssert(schemeInfo != NULL, "Default scheme not available");
scheme = schemeInfo->GetName();
if (!schemeInfo->Parse(url, *this))
return FALSE;
return !IsEmpty();
}
BOOL PURL::LegacyParse(const PString & _url, const PURLLegacyScheme * schemeInfo)
{
PString url = _url;
PINDEX pos;
// Super special case!
if (scheme *= "callto") {
// Actually not part of MS spec, but a lot of people put in the // into
// the URL, so we take it out of it is there.
if (url.GetLength() > 2 && url[0] == '/' && url[1] == '/')
url.Delete(0, 2);
// For some bizarre reason callto uses + instead of ; for paramters
// We do a loop so that phone numbers of the form +61243654666 still work
do {
pos = url.Find('+');
} while (pos != P_MAX_INDEX && isdigit(url[pos+1]));
if (pos != P_MAX_INDEX) {
SplitVars(url(pos+1, P_MAX_INDEX), paramVars, '+', '=');
url.Delete(pos, P_MAX_INDEX);
}
hostname = paramVars("gateway");
if (!hostname)
username = UntranslateString(url, LoginTranslation);
else {
PCaselessString type = paramVars("type");
if (type == "directory") {
pos = url.Find('/');
if (pos == P_MAX_INDEX)
username = UntranslateString(url, LoginTranslation);
else {
hostname = UntranslateString(url.Left(pos), LoginTranslation);
username = UntranslateString(url.Mid(pos+1), LoginTranslation);
}
}
else {
// Now look for an @ and split user and host
pos = url.Find('@');
if (pos != P_MAX_INDEX) {
username = UntranslateString(url.Left(pos), LoginTranslation);
hostname = UntranslateString(url.Mid(pos+1), LoginTranslation);
}
else {
if (type == "ip" || type == "host")
hostname = UntranslateString(url, LoginTranslation);
else
username = UntranslateString(url, LoginTranslation);
}
}
}
// Allow for [ipv6] form
pos = hostname.Find(']');
if (pos == P_MAX_INDEX)
pos = 0;
pos = hostname.Find(':', pos);
if (pos != P_MAX_INDEX) {
port = (WORD)hostname.Mid(pos+1).AsUnsigned();
portSupplied = TRUE;
hostname.Delete(pos, P_MAX_INDEX);
}
password = paramVars("password");
return TRUE;
}
// if the URL should have leading slash, then remove it if it has one
if (schemeInfo != NULL && schemeInfo->hasHostPort && schemeInfo->hasPath) {
if (url.GetLength() > 2 && url[0] == '/' && url[1] == '/')
url.Delete(0, 2);
else
relativePath = TRUE;
}
// parse user/password/host/port
if (!relativePath && schemeInfo->hasHostPort) {
PString endHostChars;
if (schemeInfo->hasPath)
endHostChars += '/';
if (schemeInfo->hasQuery)
endHostChars += '?';
if (schemeInfo->hasParameters)
endHostChars += ';';
if (schemeInfo->hasFragments)
endHostChars += '#';
if (endHostChars.IsEmpty())
pos = P_MAX_INDEX;
else
pos = url.FindOneOf(endHostChars);
PString uphp = url.Left(pos);
if (pos != P_MAX_INDEX)
url.Delete(0, pos);
else
url.MakeEmpty();
// if the URL is of type UserPasswordHostPort, then parse it
if (schemeInfo->hasUsername) {
// extract username and password
PINDEX pos2 = uphp.Find('@');
PINDEX pos3 = P_MAX_INDEX;
if (schemeInfo->hasPassword)
pos3 = uphp.Find(':');
switch (pos2) {
case 0 :
uphp.Delete(0, 1);
break;
case P_MAX_INDEX :
if (schemeInfo->defaultToUserIfNoAt) {
if (pos3 == P_MAX_INDEX)
username = UntranslateString(uphp, LoginTranslation);
else {
username = UntranslateString(uphp.Left(pos3), LoginTranslation);
password = UntranslateString(uphp.Mid(pos3+1), LoginTranslation);
}
uphp.MakeEmpty();
}
break;
default :
if (pos3 > pos2)
username = UntranslateString(uphp.Left(pos2), LoginTranslation);
else {
username = UntranslateString(uphp.Left(pos3), LoginTranslation);
password = UntranslateString(uphp(pos3+1, pos2-1), LoginTranslation);
}
uphp.Delete(0, pos2+1);
}
}
// if the URL does not have a port, then this is the hostname
if (schemeInfo->defaultPort == 0)
hostname = UntranslateString(uphp, LoginTranslation);
else {
// determine if the URL has a port number
// Allow for [ipv6] form
pos = uphp.Find(']');
if (pos == P_MAX_INDEX)
pos = 0;
pos = uphp.Find(':', pos);
if (pos == P_MAX_INDEX)
hostname = UntranslateString(uphp, LoginTranslation);
else {
hostname = UntranslateString(uphp.Left(pos), LoginTranslation);
port = (WORD)uphp.Mid(pos+1).AsUnsigned();
portSupplied = TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -