//+------------------------------------------------------------------+ //| 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= 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); } //+------------------------------------------------------------------+