MQL5Book/Include/URL.mqh

180 lines
4.6 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 16:09:41 +02:00
//+------------------------------------------------------------------+
//| URL.mqh |
//| Copyright 2022, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| URL components |
//+------------------------------------------------------------------+
enum URL_PARTS
{
URL_COMPLETE,
URL_SCHEME, // protocol
URL_USER, // deprecated/not supported/null
URL_HOST,
URL_PORT,
URL_PATH,
URL_QUERY,
URL_FRAGMENT, // not extracted/null
URL_ENUM_LENGTH
};
//+------------------------------------------------------------------+
//| URL parser |
//+------------------------------------------------------------------+
class URL
{
public:
static bool isAlpha(const uchar c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
static bool isDigit(const uchar c)
{
return c >= '0' && c <= '9';
}
static bool isAlNum(const uchar c)
{
return isAlpha(c) || isDigit(c);
}
static string encode(const string str)
{
string new_str = "";
uchar c;
uchar chars[];
const int len = StringToCharArray(str, chars);
for(int i = 0; i < len; i++)
{
c = chars[i];
if(c == ' ') new_str += "+";
else if(isAlNum(c) || c == '-' || c == '_' || c == '.' || c == '~') new_str += (string)c;
else
{
new_str += "%" + StringFormat("%%%.2X", c);
}
}
return new_str;
}
static uchar hex2value(const uchar hex)
{
uint result = hex - '0';
if(result > 9)
{
result = hex - 'A' + 10;
if(result > 15)
{
result = hex - 'a' + 10;
}
}
return (uchar)result;
}
// TODO: 2-byte/3-byte encodings support
static string decode(const string str)
{
string ret;
uchar chars[];
const int len = StringToCharArray(str, chars);
for(int i = 0; i < len; i++)
{
if(chars[i] != '%')
{
if(chars[i] == '+')
ret += " ";
else
ret += (string)chars[i];
}
else
{
ret += (string)(uchar)(hex2value(chars[i + 1]) * 16 + hex2value(chars[i + 2]));
i += 2;
}
}
return ret;
}
static string trim(string &str)
{
StringTrimLeft(str);
StringTrimRight(str);
return str;
}
// scheme://example.com:80/path?query#hash
static void parse(string url, string &parts[])
{
const static string start = "://";
const static string comma = ":";
const static string slash = "/";
const static string question = "?";
ArrayResize(parts, URL_PARTS::URL_ENUM_LENGTH);
for(int i = 0; i < URL_PARTS::URL_ENUM_LENGTH; i++)
{
parts[i] = NULL;
}
parts[0] = url; // TODO: re-assemble url from parts
int c = 0;
int p = StringFind(url, start);
if(p > -1)
{
parts[URL_SCHEME] = StringSubstr(url, 0, p);
p += StringLen(start);
c = p;
}
p = StringFind(url, comma, c);
int path = StringFind(url, slash, c);
int port = -1;
if(p > -1 && (p < path || path == -1))
{
port = p;
parts[URL_HOST] = StringSubstr(url, c, p - c);
}
if(path > -1)
{
parts[URL_HOST] = StringSubstr(url, c, (port != -1 ? port : path) - c);
c = path + 1;
}
else
{
parts[URL_HOST] = StringSubstr(url, c, (port != -1 ? port : StringLen(url)) - c);
c = StringLen(url) + 1;
}
if(port > -1)
{
parts[URL_PORT] = StringSubstr(url, port + 1, c - port - 2);
}
if(path > -1)
{
p = StringFind(url, question, path);
c = p;
if(p == -1) p = StringLen(url);
parts[URL_PATH] = StringSubstr(url, path, p - path);
if(c > -1)
{
parts[URL_QUERY] = StringSubstr(url, c + 1);
}
}
else
{
parts[URL_PATH] = "/";
}
}
};
//+------------------------------------------------------------------+