forked from airat77786/MQL5Book
179 lines
4.6 KiB
MQL5
179 lines
4.6 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| 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] = "/";
| |||
}
| |||
}
| |||
};
| |||
//+------------------------------------------------------------------+
|