Connexus/Utils/URL.mqh
2025-06-05 11:54:55 -03:00

385 lines
30 KiB
MQL5

//+------------------------------------------------------------------+
//| Define constants for different URL protocols |
//+------------------------------------------------------------------+
#define HTTP "http"
#define HTTPS "https"
#define WS "ws"
#define WSS "wss"
#define FTP "ftp"
//+------------------------------------------------------------------+
//| Include the header file that defines the CQueryParam class |
//+------------------------------------------------------------------+
#include "QueryParam.mqh"
//+------------------------------------------------------------------+
//| Enum to represent different URL protocol |
//+------------------------------------------------------------------+
enum ENUM_URL_PROTOCOL
{
URL_PROTOCOL_NULL = 0, // No protocol defined
URL_PROTOCOL_HTTP, // HTTP protocol
URL_PROTOCOL_HTTPS, // HTTPS protocol
URL_PROTOCOL_WS, // WebSocket (WS) protocol
URL_PROTOCOL_WSS, // Secure WebSocket (WSS) protocol
URL_PROTOCOL_FTP // FTP protocol
};
//+------------------------------------------------------------------+
//| class : CURL |
//| |
//| [PROPERTY] |
//| Name : CURL |
//| Heritage : No heritage |
//| Description : Define a class CURL to manage and manipulate URLs |
//| |
//+------------------------------------------------------------------+
class CURL
{
private:
//--- Structure to hold components of a URL
struct MqlURLComponents
{
ENUM_URL_PROTOCOL protocol; // URL protocol
string host; // Host name or IP
uint port; // Port number
string path; // Path after the host
CQueryParam query_param; // Query parameters as key-value pairs
};
MqlURLComponents m_url; // Instance of MqlURL to store the URL details
string ProtocolToString(ENUM_URL_PROTOCOL protocol); // Helper method to convert protocol enum to string
public:
CURL(void);
~CURL(void);
//--- Methods to access and modify URL components
ENUM_URL_PROTOCOL GetProtocol(void) const; // Get the protocol
void SetProtocol(ENUM_URL_PROTOCOL protocol); // Set the protocol
string GetHost(void) const; // Get the host
void SetHost(const string host); // Set the host
uint GetPort(void) const; // Get the port
void SetPort(const uint port); // Set the port
string GetPath(void) const; // Get the path
void SetPath(const string path); // Set the path
CQueryParam *QueryParam(void); // Access query parameters
//--- Auxiliary methods
bool IsEqualTo(CURL &url); // Compare two URLs
string ToString(); // Show URL details as a string
//--- Methods to parse and serialize the URL
void Clear(void); // Clear/reset the URL
string GetBaseUrl(void); // Return the base URL (protocol, host, port)
string GetPathAndQuery(void); // Return the path and query part of the URL
string GetFullUrl(void); // Return the complete URL
bool ParseFromString(const string url); // Parse a URL string into components
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CURL::CURL(void)
{
this.Clear();
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CURL::~CURL(void)
{
}
//+------------------------------------------------------------------+
//| Convert URL protocol enum to string |
//+------------------------------------------------------------------+
string CURL::ProtocolToString(ENUM_URL_PROTOCOL protocol)
{
if(protocol == URL_PROTOCOL_HTTP) { return(HTTP); }
if(protocol == URL_PROTOCOL_HTTPS) { return(HTTPS); }
if(protocol == URL_PROTOCOL_WS) { return(WS); }
if(protocol == URL_PROTOCOL_WSS) { return(WSS); }
if(protocol == URL_PROTOCOL_FTP) { return(FTP); }
return(NULL);
}
//+------------------------------------------------------------------+
//| Getter for protocol |
//+------------------------------------------------------------------+
ENUM_URL_PROTOCOL CURL::GetProtocol(void) const
{
return(m_url.protocol);
}
//+------------------------------------------------------------------+
//| Setter for protocol |
//+------------------------------------------------------------------+
void CURL::SetProtocol(ENUM_URL_PROTOCOL protocol)
{
m_url.protocol = protocol;
}
//+------------------------------------------------------------------+
//| Getter for host |
//+------------------------------------------------------------------+
string CURL::GetHost(void) const
{
return(m_url.host);
}
//+------------------------------------------------------------------+
//| Setter for host |
//+------------------------------------------------------------------+
void CURL::SetHost(const string host)
{
m_url.host = host;
}
//+------------------------------------------------------------------+
//| Getter for port |
//+------------------------------------------------------------------+
uint CURL::GetPort(void) const
{
return(m_url.port);
}
//+------------------------------------------------------------------+
//| Setter for port |
//+------------------------------------------------------------------+
void CURL::SetPort(const uint port)
{
m_url.port = port;
}
//+------------------------------------------------------------------+
//| Getter for path |
//+------------------------------------------------------------------+
string CURL::GetPath(void) const
{
return(m_url.path);
}
//+------------------------------------------------------------------+
//| Setter for path |
//+------------------------------------------------------------------+
void CURL::SetPath(const string path)
{
m_url.path = path;
}
//+------------------------------------------------------------------+
//| Accessor for query parameters (returns a pointer) |
//+------------------------------------------------------------------+
CQueryParam *CURL::QueryParam(void)
{
return(GetPointer(m_url.query_param));
}
//+------------------------------------------------------------------+
//| Compare the current URL with another URL |
//+------------------------------------------------------------------+
bool CURL::IsEqualTo(CURL &url)
{
return (m_url.protocol == url.GetProtocol() &&
m_url.host == url.GetHost() &&
m_url.port == url.GetPort() &&
m_url.path == url.GetPath() &&
m_url.query_param.ToString() == url.QueryParam().ToString());
}
//+------------------------------------------------------------------+
//| Display the components of the URL as a formatted string |
//+------------------------------------------------------------------+
string CURL::ToString(void)
{
return(
"Protocol: "+EnumToString(m_url.protocol)+"\n"+
"Host: "+m_url.host+"\n"+
"Port: "+IntegerToString(m_url.port)+"\n"+
"Path: "+m_url.path+"\n"+
"Query Param: "+m_url.query_param.ToString()+"\n"
);
}
//+------------------------------------------------------------------+
//| Clear or reset the URL structure |
//+------------------------------------------------------------------+
void CURL::Clear(void)
{
m_url.protocol = URL_PROTOCOL_NULL;
m_url.host = "";
m_url.port = 0;
m_url.path = "";
m_url.query_param.Clear();
}
//+------------------------------------------------------------------+
//| Construct the base URL from protocol, host, and port |
//+------------------------------------------------------------------+
string CURL::GetBaseUrl(void)
{
//--- Checks if host is not null or empty
if(m_url.host != "" && m_url.host != NULL)
{
MqlURLComponents url = m_url;
//--- Set default protocol if not defined
if(url.protocol == URL_PROTOCOL_NULL)
{
url.protocol = URL_PROTOCOL_HTTPS;
}
//--- Set default port based on the protocol
if(url.port == 0)
{
url.port = (url.protocol == URL_PROTOCOL_HTTPS) ? 443 : 80;
}
//--- Construct base URL (protocol + host)
string serialized_url = this.ProtocolToString(url.protocol) + "://" + url.host;
//--- Include port in URL only if it's not the default port for the protocol
if(!(url.protocol == URL_PROTOCOL_HTTP && url.port == 80) &&
!(url.protocol == URL_PROTOCOL_HTTPS && url.port == 443))
{
serialized_url += ":" + IntegerToString(m_url.port);
}
return(serialized_url);
}
else
{
return("Error: Invalid host");
}
}
//+------------------------------------------------------------------+
//| Construct path and query string from URL components |
//+------------------------------------------------------------------+
string CURL::GetPathAndQuery(void)
{
MqlURLComponents url = m_url;
//--- Ensure path starts with a "/"
if(url.path == "")
{
url.path = "/";
}
else if(StringGetCharacter(url.path,0) != '/')
{
url.path = "/" + url.path;
}
//--- Remove any double slashes from the path
StringReplace(url.path,"//","/");
//--- Check for invalid spaces in the path
if(StringFind(url.path," ") >= 0)
{
return("Error: Invalid characters in path");
}
//--- Return the full path and query string
return(url.path + url.query_param.ToString());
}
//+------------------------------------------------------------------+
//| Return the complete URL (base URL + path + query) |
//+------------------------------------------------------------------+
string CURL::GetFullUrl(void)
{
return(this.GetBaseUrl() + this.GetPathAndQuery());
}
//+------------------------------------------------------------------+
//| Parse a URL string and extract its components |
//+------------------------------------------------------------------+
bool CURL::ParseFromString(const string url)
{
//--- Create an instance of MqlURL to hold the parsed data
MqlURLComponents urlObj;
//--- Parse protocol from the URL
int index_end_protocol = 0;
//--- Check if the URL starts with "http://"
if(StringFind(url,"http://") >= 0)
{
urlObj.protocol = URL_PROTOCOL_HTTP;
index_end_protocol = 7;
}
else if(StringFind(url,"https://") >= 0)
{
urlObj.protocol = URL_PROTOCOL_HTTPS;
index_end_protocol = 8;
}
else if(StringFind(url,"ws://") >= 0)
{
urlObj.protocol = URL_PROTOCOL_WS;
index_end_protocol = 5;
}
else if(StringFind(url,"wss://") >= 0)
{
urlObj.protocol = URL_PROTOCOL_WSS;
index_end_protocol = 6;
}
else if(StringFind(url,"ftp://") >= 0)
{
urlObj.protocol = URL_PROTOCOL_FTP;
index_end_protocol = 6;
}
else
{
return(false); // Unsupported protocol
}
//--- Separate the endpoint part after the protocol
string endpoint = StringSubstr(url,index_end_protocol); // Get the URL part after the protocol
string parts[]; // Array to hold the split components of the URL
//--- Split the endpoint by the "/" character to separate path and query components
int size = StringSplit(endpoint,StringGetCharacter("/",0),parts);
//--- Handle the host and port part of the URL
string host_port[];
//--- If the first part (host) contains a colon (":"), split it into host and port
if(StringSplit(parts[0],StringGetCharacter(":",0),host_port) > 1)
{
urlObj.host = host_port[0]; // Set the host
urlObj.port = (uint)StringToInteger(host_port[1]); // Convert and set the port
}
else
{
urlObj.host = parts[0];
//--- Set default port based on the protocol
if(urlObj.protocol == URL_PROTOCOL_HTTP)
{
urlObj.port = 80;
}
if(urlObj.protocol == URL_PROTOCOL_HTTPS)
{
urlObj.port = 443;
}
}
//--- If there's no path, default to "/"
if(size == 1)
{
urlObj.path += "/"; // Add a default root path "/"
}
//--- Loop through the remaining parts of the URL (after the host)
for(int i=1;i<size;i++)
{
//--- If the path contains an empty part, return false (invalid URL)
if(parts[i] == "")
{
return(false);
}
//--- If the part contains a "?" (indicating query parameters)
else if(StringFind(parts[i],"?") >= 0)
{
string resource_query[];
//--- Split the part by "?" to separate the resource and query
if(StringSplit(parts[i],StringGetCharacter("?",0),resource_query) > 0)
{
urlObj.path += "/"+resource_query[0];
urlObj.query_param.ParseQueryString(resource_query[1]);
}
}
else
{
//--- Otherwise, add to the path as part of the URL
urlObj.path += "/"+parts[i];
}
}
//--- Assign the parsed URL object to the member variable
m_url = urlObj;
return(true);
}
//+------------------------------------------------------------------+