English Русский 中文 Deutsch 日本語
preview
Encabezado en Connexus (Parte 3): Dominando el uso de encabezado HTTP para solicitudes WebRequest

Encabezado en Connexus (Parte 3): Dominando el uso de encabezado HTTP para solicitudes WebRequest

MetaTrader 5Ejemplos | 2 junio 2025, 09:25
106 0
joaopedrodev
joaopedrodev

Introducción

Este artículo es la continuación de una serie de artículos donde construiremos una biblioteca llamada Connexus. En el primer artículo, comprendimos el funcionamiento básico de la función WebRequest, entendiendo cada uno de sus parámetros y también creamos un código de ejemplo que demuestra el uso de esta función y sus dificultades. En este artículo, exploraremos la importancia y la utilidad de los encabezados en la comunicación HTTP, y cómo estos elementos se utilizan para diferentes propósitos en la web moderna.

La estructura de un mensaje HTTP, ya sea una respuesta o una petición, se compone de dos elementos fundamentales en los que profundizaremos: cabeceras y cuerpo. Cada uno de ellos tiene su papel en el proceso de comunicación, garantizando que los datos se transmitan de forma organizada, eficiente y segura.

Para comenzar, recapitulemos brevemente cómo funciona una estructura de solicitud y respuesta HTTP.


Estructura de una solicitud HTTP

Una solicitud HTTP normalmente sigue este formato:

HTTP Method | URL | HTTP Version
Headers
Body (Optional)
  • HTTP Method: Define la intención de la petición (GET, POST, PUT, DELETE, etc.).
  • URL: Identifica el recurso solicitado. (Ya hablamos de ello con más detalle en el último artículo).
  • HTTP Version: Especifica la versión del protocolo que se está utilizando.
  • Headers: Metadatos de la solicitud, como tipo de contenido, autenticación, etc.
  • Body: El contenido de la solicitud, normalmente presente en métodos como POST o PUT.


Estructura de una respuesta HTTP

Una respuesta HTTP sigue una estructura similar:

HTTP Version | Status Code | Status Message
Headers
Body (Optional)
  • Status Code: Indica el resultado (200 OK, 404 Not Found, 500 Internal Server Error, etc.).
  • Headers: Información sobre el contenido devuelto, como tamaño, tipo de datos, etc.
  • Body: El contenido real de la respuesta, como el HTML de una página o datos JSON. En nuestro caso, será más común recibir datos JSON, pero tenga en cuenta que algunas API pueden devolver HTML.

En el último artículo profundizamos en el formato de una URL, entendiendo cada elemento de forma aislada y agrupándolos para formar una dirección completa. En este artículo profundizaremos en el encabezado de una solicitud HTTP. ¿Para qué sirve? ¿Cómo usarlo? ¿Cuáles son los posibles valores, etc.?


Encabezados

Primero, entendamos qué son los encabezados. En el protocolo HTTP, un encabezado es un conjunto de datos adicionales que se envían con la solicitud o respuesta. Los encabezados HTTP son funciones esenciales en las comunicaciones cliente-servidor. Su propósito principal es proporcionar detalles sobre la solicitud que no son parte directa de la URL o del cuerpo del mensaje. Ayudan a controlar el flujo de comunicación y proporcionan contexto para garantizar que el servidor interprete y procese los datos correctamente. Estos encabezados sirven para ayudar tanto al servidor como al cliente a comprender mejor el contexto de la solicitud o respuesta, como el tipo de contenido, el almacenamiento en caché, la autenticación, entre otros. En otras palabras, actúan como metadatos que informan al servidor sobre cómo se puede procesar la solicitud y al cliente cómo se debe interpretar la respuesta.

Mencionemos algunas de las principales funciones de los encabezados HTTP:

  1. Autenticación: Uno de los usos más comunes de las cabeceras es autenticar al cliente, de forma que el servidor sepa quién envía la petición y si tiene acceso a la información. Por ejemplo, el encabezado de autorización envía un token o credenciales que el servidor recibe y puede usar para validar al cliente antes de procesar la solicitud.
  2. Control de caché: Las cabeceras como Cache-Control permiten al cliente y al servidor configurar cómo se pueden almacenar en caché los datos, lo que puede ser útil para evitar otra petición innecesaria del cliente al servidor o del servidor a algún otro servicio. Los datos en caché se pueden almacenar en el cliente, en servidores proxy o en otros puntos intermedios.
  3. Especificación del tipo de contenido: La cabecera Content-Type permite al cliente informar al servidor del tipo de datos que se están enviando o recibiendo. Normalmente se utilizan formatos como JSON, XML o HTML. Esto garantiza que ambas partes de la comunicación sepan cómo interpretar correctamente los datos.
  4. Negociación de contenido: El cliente puede utilizar la cabecera Accept para informar al servidor de qué formatos de respuesta son aceptables, como application/json (el servidor enviará los datos en formato json) o text/html (el servidor los enviará en formato html). Esto permite que el servidor envíe la respuesta en un formato que el cliente esté preparado para recibir. El formato más utilizado es JSON y en él nos centraremos aquí, pero hay otros formatos compatibles.
  5. Seguridad: Cabeceras como Strict-Transport-Security ayudan a reforzar el uso de HTTPS (HTTPS es lo mismo que HTTP, pero contiene una capa adicional de seguridad web. La solicitud, respuesta, encabezados, cuerpo, URL y otros formatos son exactamente los mismos, por lo que se recomienda utilizar siempre HTTPS). Otros encabezados como CORS (Cross Origin Resource Sharing) definen qué dominios pueden acceder a los recursos de una API, lo que aumenta la seguridad. De esta manera, el servidor está filtrando a quién envía la información, enviándola únicamente a un dominio previamente definido, de manera que nadie ajeno a ese dominio pueda acceder a los datos.
  6. Limitación de velocidad: Algunos servicios devuelven cabeceras como X-RateLimit-Limit y X-RateLimit-Reaming para informar al cliente de cuántas peticiones están permitidas en un periodo de tiempo determinado. Esto evita que una gran cantidad de solicitudes sobrecarguen el servidor.

Por lo tanto, los encabezados juegan un papel crucial en la comunicación HTTP, proporcionando control, claridad y seguridad sobre cómo se debe manejar la solicitud y la respuesta.


Conociendo algunos valores posibles

Veamos cuáles son los valores posibles de los encabezados más comunes. Los encabezados HTTP son muy versátiles y se pueden personalizar según sea necesario para la comunicación entre el cliente y el servidor. Comprenda que los valores que puede contener un encabezado dependen del contexto de la comunicación. A continuación se muestran algunos ejemplos de los encabezados más comunes y sus posibles valores:

  1. Autorización: Se utiliza para enviar credenciales o tokens de autenticación del cliente al servidor, permitiendo el acceso a recursos protegidos. Puede asumir diferentes valores dependiendo del método de autenticación utilizado. Existen varios métodos de autenticación, veamos los más utilizados:
    • Token portador: Uno de los formatos más comunes, especialmente en APIs modernas que utilizan autenticación basada en token. Este valor normalmente es un token JWT (JSON Web Token) que el cliente recibe después de iniciar sesión o autenticarse en un servidor de autenticación.

      Authorization: Bearer <my_token_jwt>

      El valor <my_token_jwt> es el propio token de autenticación, normalmente una cadena larga de caracteres codificados en Base64. Este token contiene información de autorización del usuario y tiene una validez limitada.

    • Basic: este valor utiliza la autenticación básica HTTP, en la que el nombre de usuario y la contraseña se codifican en Base64 y se envían directamente en la cabecera. Este método es menos seguro, ya que las credenciales se pueden decodificar fácilmente y no deben utilizarse sin HTTPS.

      Authorization: Basic base64(username:password)

      El valor base64(usuario:contraseña) es la codificación en Base64 del par usuario:contraseña. Aunque este método es sencillo, es vulnerable a los ataques a menos que se utilice a través de conexiones cifradas.

    • Digest: Un método más seguro que Basic , Digest utiliza un hash de las credenciales del usuario en lugar de enviar las credenciales directamente. Aunque es menos común hoy en día con el auge de OAuth y JWT, todavía se encuentra en algunas API.

      Authorization: Digest username="admin", realm="example", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", response="6629fae49393a05397450978507c4ef1"

      Aquí, el valor contiene varios campos, como el nombre de usuario, el nonce (un número usado una vez), el ámbito (alcance de la autenticación) y la respuesta cifrada con hash.

  1. Content-Type: Esta cabecera define el tipo de contenido presente en el cuerpo de la petición o respuesta. Veamos algunos de los valores posibles más utilizados:
    • application/json: Este es el valor más común cuando se trata de APIs, ya que JSON es un formato ligero y fácil de leer y escribir para transferir datos. Ejemplo de uso:

      Content-Type: application/json

    • application/xml: XML (Extensible Markup Language) fue ampliamente utilizado antes de JSON, y todavía se utiliza en algunos sistemas heredados o APIs antiguas. Pero la mayoría de las API actuales admiten el formato JSON, así que no te preocupes por XML por ahora.

      Content-Type: application/xml

    • multipart/form-data: Este valor se utiliza para enviar datos mixtos, especialmente cuando el cliente necesita cargar archivos o datos de formulario. No es nuestro enfoque aquí por ahora, pero es bueno saber que existe esta posibilidad.

    • text/html: Se utiliza cuando el contenido a enviar o recibir es HTML. Este valor es común en solicitudes y respuestas de páginas web.

      Content-Type: text/html

  1. User-Agent: Esta cabecera la utiliza el cliente para identificarse. Generalmente contiene datos sobre el tipo de dispositivo, navegador y sistema operativo que realiza la solicitud. No existe una regla específica para los valores aquí, pero se utilizan algunos estándares:
    • Navegadores web: El valor enviado por los navegadores suele contener detalles sobre el nombre y la versión del navegador, así como sobre el sistema operativo.

      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36

    • APIs o Aplicacioness: Las aplicaciones pueden utilizar el valor personalizado, como:

      User-Agent: Connexus/1.0 (MetaTrader 5 Terminal)

      Esto permite al servidor saber que la solicitud procede de una aplicación específica, lo que facilita la depuración o la supervisión en caso necesario.

  1. Accept: Lo utiliza el cliente para informar qué contenido acepta como respuesta. Los valores posibles para este encabezado son similares a Content-Type, veamos algunos de ellos:
    • application/json, application/xml, text/html: Ya los conoce, los vimos en la sección Content-Type.

    • image/png, image/jpeg, image/webp: Estos valores se utilizan cuando el cliente espera recibir una imagen. Por ejemplo, en API que proporcionan datos gráficos, como miniaturas o gráficos generados. O simplemente imágenes como el avatar de un usuario, o el logotipo del sitio web, etc.

      Accept: image/png, image/jpeg

      Aquí, el cliente informa que acepta imágenes PNG y JPEG.

    • */*: Este valor indica que el cliente acepta cualquier tipo de contenido como respuesta. Se utiliza cuando el cliente no tiene preferencia por un formato específico o está preparado para afrontar cualquier tipo de respuesta.



¿Cómo organizar el encabezado?

Ahora que entendemos para qué sirven los encabezados y algunos valores posibles, entendamos cómo se organizan. Los encabezados se organizan como un conjunto de pares de clave y valor y se insertan en las solicitudes. Aunque algunos encabezados son obligatorios para algunas operaciones, la mayoría de ellos son opcionales y dependen de las necesidades de la aplicación. Ejemplo de un encabezado básico para una solicitud HTTP:

GET /api/resource HTTP/1.1
Host: example.com
Authorization: Bearer token123
Content-Type: application/json
User-Agent: Connexus/1.0 (MetaTrader 5 Terminal)

Como se muestra en este ejemplo, tenemos 3 encabezados:

  • Authorization: Proporciona nuestro token de acceso “token123”
  • Content-Type: Proporciona información sobre cómo se envían los datos, utilizamos JSON
  • User-Agent: Proporciona información al servidor para que éste sepa cómo procesar la solicitud. En este ejemplo, utilizamos “Connexus/1.0 (MetaTrader 5 Terminal)”

Estos encabezados proporcionan información para que el servidor sepa cómo procesar la solicitud. Este es solo un ejemplo simple, y pronto entraremos en más detalles sobre qué conjuntos se usan más comúnmente, los valores posibles y para qué sirve cada uno.


Pongámonos con el código

Ya entendemos cómo funcionan los encabezados, para qué sirven y cómo utilizarlos, ahora vamos a la parte práctica. ¿Recuerdas httpbin? Es un servicio gratuito que simplemente funciona como un espejo, todo lo que enviamos nos regresa nuevamente, usaremos esto para verificar que encabezados estamos enviando, y si hay algún encabezado que la propia terminal agregue automáticamente. Para hacer esto, crearé un archivo llamado TestHeader.mq5 en la carpeta Experts/Connexus/Test/TestHeader.mq5. Vamos a crear una solicitud POST sin enviar nada en el encabezado y veamos qué nos responde:

#include <Connexus2/Data/Json.mqh>
#include <Connexus2/URL/URL.mqh>

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- URL
   CURL url;
   url.Parse("https://httpbin.org");
   url.Path("post");
   
   //--- Data to be sent
   string method = "POST";
   char body_send[];
   string headers_send;
   
   //--- Data that will be received
   char body_receive[];
   string headers_receive;
   
   //--- Send request
   int status_code = WebRequest(method,url.FullUrl(),headers_send,5000,body_send,body_receive,headers_receive);
   
   //--- Show response
   Print("Respose: ",CharArrayToString(body_receive));
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Al ejecutar este código en el terminal, tendremos esta respuesta:

Respose: {
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Accept-Language": "pt,en;q=0.5", 
    "Content-Length": "0", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "MetaTrader 5 Terminal/5.4518 (Windows NT 11.0.22631; x64)", 
    "X-Amzn-Trace-Id": "Root=1-66feb3d9-50de44d019af8b0c1058436b"
  }, 
  "json": null, 
  "origin": "189.74.63.39", 
  "url": "https://httpbin.org/post"
}

Tenga en cuenta el valor de “headers”, que contiene otro objeto json con algunos valores de encabezado que la terminal define automáticamente. Recuerda que en la solicitud no enviamos ningún dato, ni en el encabezado ni en el cuerpo. Entendamos cada uno de estos encabezados que se envían automáticamente:

  • Accept: Como vimos anteriormente, indica al servidor qué tipos de contenido está dispuesto a aceptar el cliente como respuesta. En este caso, el valor */* como se vio anteriormente, significa que el cliente acepta cualquier tipo de contenido como respuesta.

  • Accept-Encoding: Especifica los tipos de codificación de contenido que el cliente puede aceptar. Las codificaciones se utilizan para comprimir datos con el fin de ahorrar ancho de banda en la red.

    • gzip: Se trata de un formato de compresión utilizado para reducir el tamaño de la respuesta enviada.
    • deflate: Es otra forma de compresión de datos similar a gzip, pero con algunas diferencias técnicas en el algoritmo.
  • Accept-Language: Esta cabecera indica al servidor qué idiomas prefiere el cliente, pero el servidor debe admitir varios idiomas. Ayuda al servidor a ofrecer contenidos en el idioma más apropiado para el usuario.

    • pt: El cliente prefiere recibir la respuesta en portugués.
    • en;q=0.5: Aquí, se representa el inglés, y q=0,5 sería un «factor de calidad» (de 0 a 1) que indica la prioridad relativa de la lengua. Un valor de q=1,0 sería la máxima preferencia, mientras que q=0,5 informa de que el cliente acepta el inglés, pero prefiere el portugués.
  • Content-Length: Indica el tamaño del cuerpo de la solicitud en bytes. En este caso el valor es 0, lo que significa que no hay contenido, como dijimos, no enviamos datos en el cuerpo de la solicitud.

  • Content-Type: Informa al servidor del tipo de datos que se envían al servidor. En este caso, el valor application/x-www-form-urlencoded significa que los datos se enviaron en formato de formulario codificado en URL. No estoy seguro de por qué MetaTrader 5 establece este formato de forma predeterminada.

  • Host: Especifica el nombre del servidor al que se envía la solicitud. Esto es necesario para que el servidor sepa qué dominio o IP se está solicitando, especialmente si está sirviendo múltiples dominios en una única dirección IP.

  • User-Agent: Es una cadena que identifica al cliente HTTP (en este caso, MetaTrader 5) que está realizando la petición. Contiene detalles sobre el software y el sistema operativo que se utiliza.

  • X-Amzn-Trace-Id: El encabezado X-Amzn-Trace-Id es utilizado por los servicios de AWS (Amazon Web Services) para rastrear solicitudes a través de su infraestructura distribuida. Ayuda a identificar y depurar problemas en las aplicaciones que se ejecutan en la nube de Amazon.

    • Root=1-66feb3d9-50de44d019af8b0c1058436b: Este valor representa un ID de seguimiento único que puede utilizarse para identificar la transacción específica. Es útil para el diagnóstico y la supervisión del rendimiento.
    • Meaning: Este identificador es asignado automáticamente por el sistema de AWS y puede utilizarse para seguir la ruta de una solicitud a medida que pasa por diferentes servicios dentro de la infraestructura de Amazon.
    • Common usage: Este encabezado es utilizado internamente por los servicios de AWS para recopilar información de seguimiento y monitorización. Es especialmente útil en arquitecturas de microservicios para diagnosticar cuellos de botella y problemas de latencia.

    Creo que MetaTrader 5 añade esto automáticamente para que pueda realizar un seguimiento de las solicitudes realizadas por el terminal para el diagnóstico como la plataforma o para ayudar a solucionar un error.

Ahora que hemos visto que cabeceras son añadidas por defecto por MetaTrader 5, vamos a cambiar algunas de ellas. Recuerde que las cabeceras no pueden tener dos claves idénticas, es decir, no puede utilizar Content-Type: application/json y Content-Type: application/x-www-form-urlencoded . Por lo tanto, si definimos el valor de Content-Type sobrescribirá el valor antiguo. Cambiemos algunos datos, definiendo otros valores para las siguientes cabeceras:

  • Content-Type: Content-Type: application/json
  • User-Agent: Connexus/1.0 (MetaTrader 5 Terminal)

Añadamos estos valores a la variable «headers_send», que es una cadena. Recuerde añadir la «\n» para separar las cabeceras entre sí. Aquí está el código modificado:

#include <Connexus2/Data/Json.mqh>
#include <Connexus2/URL/URL.mqh>

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- URL
   CURL url;
   url.Parse("https://httpbin.org");
   url.Path("post");
   
   //--- Data to be sent
   string method = "POST";
   char body_send[];
   
   //--- Headers that will be sent separated by "\n"
   string headers_send = "User-Agent: Connexus/1.0 (MetaTrader 5 Terminal)\nContent-Type: application/json";
   
   //--- Data that will be received
   char body_receive[];
   string headers_receive;
   
   //--- Send request
   int status_code = WebRequest(method,url.FullUrl(),headers_send,5000,body_send,body_receive,headers_receive);
   
   //--- Show response
   Print("Respose: ",CharArrayToString(body_receive));
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Al ejecutar el código tendremos este resultado:

Respose: {
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Accept-Language": "pt,en;q=0.5", 
    "Content-Length": "0", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "Connexus/1.0 (MetaTrader 5 Terminal)", 
    "X-Amzn-Trace-Id": "Root=1-66feb90f-037374b15f220d3e28e1cb32"
  }, 
  "json": null, 
  "origin": "189.74.63.39", 
  "url": "https://httpbin.org/post"
}

Tenga en cuenta que cambiamos los valores de User-Agent y Content-Type a los que definimos. Ahora que tenemos un ejemplo simple de una solicitud que envía algunos encabezados personalizados, agreguemos esta función de encabezados a nuestra biblioteca. Nuestro objetivo es crear una clase para trabajar con encabezados. Esta clase debe ser fácil de usar, tener una interfaz simple e intuitiva y poder agregar, eliminar o actualizar encabezados fácilmente.


Creando la clase HttpHeaders

Vamos a crear una nueva carpeta dentro de la carpeta Connexus, que está dentro de Includes. Esta nueva carpeta se llamará Headers, y dentro de esta nueva carpeta creamos un nuevo archivo llamado HttpHeaders.mqh. Al final se verá así:

Ruta

El archivo debe tener una clase vacía, similar a esta. Añadí algunos comentarios:
//+------------------------------------------------------------------+
//|                                                       Header.mqh |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| class : CHttpHeader                                              |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CHttpHeader                                        |
//| Heritage    : No heritage                                        |
//| Description : Responsible for organizing and storing the headers |
//|               of a request.                                      |
//|                                                                  |
//+------------------------------------------------------------------+
class CHttpHeader
  {
public:
                     CHttpHeader(void);
                    ~CHttpHeader(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CHttpHeader::CHttpHeader(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CHttpHeader::~CHttpHeader(void)
  {
  }
//+------------------------------------------------------------------+

Para almacenar estos encabezados utilizaremos un objeto json, donde la clave json será la clave del encabezado, y de la misma manera el valor json será el valor del encabezado. Para hacer esto, importaremos la clase json y crearemos una nueva instancia llamada m_headers dentro de la clase.

//+------------------------------------------------------------------+
//| Include the file CJson class                                     |
//+------------------------------------------------------------------+
#include "../Data/Json.mqh"
//+------------------------------------------------------------------+
//| class : CHttpHeader                                              |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CHttpHeader                                        |
//| Heritage    : No heritage                                        |
//| Description : Responsible for organizing and storing the headers |
//|               of a request.                                      |
//|                                                                  |
//+------------------------------------------------------------------+
class CHttpHeader
  {
private:
   
   CJson             m_headers;
   
public:
                     CHttpHeader(void);
                    ~CHttpHeader(void);
  };
//+------------------------------------------------------------------+

Con el objeto json listo para almacenar los datos, el siguiente paso será definir qué métodos deberá tener esta clase. Inicialmente, crearemos los siguientes métodos:

  • Add(string key, string value) : Añade una nueva cabecera a la petición HTTP o la actualiza si ya existe.
  • Get(string key) : Devuelve el valor de un encabezado específico, dado su nombre.
  • Remove(string key) : Elimina una cabecera específica.
  • Has(string key) : Comprueba si existe una cabecera con la clave especificada.
  • Clear() : Elimina todas las cabeceras de la solicitud.
  • Count() : Devuelve el número de cabeceras.

Vamos a añadir estos métodos que son más simples a la clase

//+------------------------------------------------------------------+
//| class : CHttpHeader                                              |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CHttpHeader                                        |
//| Heritage    : No heritage                                        |
//| Description : Responsible for organizing and storing the headers |
//|               of a request.                                      |
//|                                                                  |
//+------------------------------------------------------------------+
class CHttpHeader
  {
private:
   
   CJson             m_headers;
   
public:
                     CHttpHeader(void);
                    ~CHttpHeader(void);
   
   //--- Functions to manage headers
   void              Add(string key, string value);      // Adds a new header to the HTTP request or updates it if it already exists
   string            Get(string key);                    // Returns the value of a specific header, given its name.
   void              Remove(string key);                 // Removes a specific header.
   bool              Has(string key);                    // Checks whether a header with the specified key is present.
   void              Clear(void);                        // Removes all headers from the request.
   int               Count(void);                        // Returns the number of headers.
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CHttpHeader::CHttpHeader(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CHttpHeader::~CHttpHeader(void)
  {
  }
//+------------------------------------------------------------------+
//| Adds a new header to the HTTP request or updates it if it already|
//| exists                                                           |
//+------------------------------------------------------------------+
void CHttpHeader::Add(string key,string value)
  {
   m_headers[key] = value;
  }
//+------------------------------------------------------------------+
//| Returns the value of a specific header, given its name.          |
//+------------------------------------------------------------------+
string CHttpHeader::Get(string key)
  {
   return(m_headers[key].ToString());
  }
//+------------------------------------------------------------------+
//| Removes a specific header.                                       |
//+------------------------------------------------------------------+
void CHttpHeader::Remove(string key)
  {
   m_headers.Remove(key);
  }
//+------------------------------------------------------------------+
//| Checks whether a header with the specified key is present.       |
//+------------------------------------------------------------------+
bool CHttpHeader::Has(string key)
  {
   return(m_headers.FindKey(key) != NULL);
  }
//+------------------------------------------------------------------+
//| Removes all headers from the request.                            |
//+------------------------------------------------------------------+
void CHttpHeader::Clear(void)
  {
   m_headers.Clear();
  }
//+------------------------------------------------------------------+
//| Returns the number of headers.                                   |
//+------------------------------------------------------------------+
int CHttpHeader::Count(void)
  {
   return(m_headers.Size());
  }
//+------------------------------------------------------------------+

Ahora que hemos agregado los métodos más simples, agreguemos los dos últimos métodos que son el corazón de la clase, son:

  • Serialize() : Devuelve todas las cabeceras en formato cadena, listas para ser enviadas en una petición HTTP.
  • Parse(string headers) : Convierte una cadena que contiene cabeceras (normalmente recibidas en una respuesta HTTP) en un formato utilizable en la clase.

//+------------------------------------------------------------------+
//| class : CHttpHeader                                              |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CHttpHeader                                        |
//| Heritage    : No heritage                                        |
//| Description : Responsible for organizing and storing the headers |
//|               of a request.                                      |
//|                                                                  |
//+------------------------------------------------------------------+
class CHttpHeader
  {
private:
   
   CJson             m_headers;
   
public:
                     CHttpHeader(void);
                    ~CHttpHeader(void);
   
   //--- Auxiliary methods
   string            Serialize(void);                    // Returns all headers in string format, ready to be sent in an HTTP request.
   bool              Parse(string headers);              // Converts a string containing headers (usually received in an HTTP response) into a format usable by the class.
  };
//+------------------------------------------------------------------+
//| Returns all headers in string format, ready to be sent in an HTTP|
//| request.                                                         |
//+------------------------------------------------------------------+
string CHttpHeader::Serialize(void)
  {
   //--- String with the result
   string headers;
   
   //--- Get size
   int size = this.Count();
   for(int i=0;i<size;i++)
     {
      //--- Adds the header to the string in the format: "key: value"
      headers += m_headers[i].m_key + ": " + m_headers[i].ToString();
      
      //--- If it's not the last time it adds "\n" at the end of the string
      if(i != size -1)
        {
         headers += "\n";
        }
     }
   
   //--- Return result
   return(headers);
  }
//+------------------------------------------------------------------+
//| Converts a string containing headers (usually received in an HTTP|
//| response) into a format usable by the class.                     |
//+------------------------------------------------------------------+
bool CHttpHeader::Parse(string headers)
  {
   //--- Array to store the key value sets
   string params[];
   
   //--- Separate the string, using the "\n" character as a separator
   int size = StringSplit(headers,StringGetCharacter("\n",0),params);
   for(int i=0;i<size;i++)
     {
      //--- With the header separated using ": "
      int pos = StringFind(params[i],": ");
      if(pos >= 0)
        {
         //--- Get key and value
         string key = StringSubstr(params[i],0,pos);
         string value = StringSubstr(params[i],pos+2);
         
         //--- Clear value
         StringTrimRight(value);
         
         //--- Add in json
         this.Add(key,value);
        }
     }
   return(true);
  }
//+------------------------------------------------------------------+


Pruebas

Para realizar las pruebas de clase, utilizaré el mismo archivo que creamos al principio del artículo, TestHeader.mq5. Importe el archivo HttpHeader, luego cree una instancia de la clase CHttpHeader y pase los datos a la clase. Luego uso la función Serialize() para formatearlo como una cadena.

#include <Connexus/Data/Json.mqh>
#include <Connexus/URL/URL.mqh>
#include <Connexus/Header/HttpHeader.mqh>
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- URL
   CURL url;
   url.Parse("https://httpbin.org");
   url.Path("post");
   
   //--- Data to be sent
   string method = "POST";
   char body_send[];
   
   //--- Headers
   CHttpHeader headers_send;
   headers_send.Add("User-Agent","Connexus/1.0 (MetaTrader 5 Terminal)");
   headers_send.Add("Content-Type","application/json");
   
   //--- Data that will be received
   char body_receive[];
   string headers_receive;
   
   //--- Send request
   int status_code = WebRequest(method,url.FullUrl(),headers_send.Serialize(),5000,body_send,body_receive,headers_receive);
   
   //--- Show response
   Print("Respose: ",CharArrayToString(body_receive));
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Al ejecutar el código en la terminal, obtenemos la misma respuesta:

Respose: {
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Accept-Language": "pt,en;q=0.5", 
    "Content-Length": "0", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "Connexus/1.0 (MetaTrader 5 Terminal)", 
    "X-Amzn-Trace-Id": "Root=1-66fed891-0d6adb5334becd71123795c9"
  }, 
  "json": null, 
  "origin": "189.74.63.39", 
  "url": "https://httpbin.org/post"
}


Conclusión

En resumen, los encabezados HTTP son como las pequeñas notas que pasas durante la clase para que el servidor sepa qué hacer con tu solicitud. Pueden autenticar, establecer el tipo de contenido, dar instrucciones sobre el almacenamiento en caché y mucho más. Sin ellos, la comunicación HTTP sería tan caótica como intentar pedir un café sin especificar el tamaño, la cantidad de azúcar o el tipo de leche. Actualmente, el diagrama de clases que hemos creado hasta ahora se ve así:

Diagrama Connexus

Ahora que comprende los encabezados y lo esenciales que son, es hora de abordar algo aún más interesante: el cuerpo de la solicitud. En el próximo artículo, profundizaremos en el corazón de la comunicación HTTP: el contenido real que desea enviar al servidor. Después de todo, no tiene sentido enviar una nota sin contenido, ¿verdad?

Prepárese, el próximo capítulo explorará cómo empaquetar esta información de manera elegante y efectiva en el cuerpo. ¡Nos vemos allí!

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16043

Desarrollo de un sistema de repetición (Parte 78): Un nuevo Chart Trade (V) Desarrollo de un sistema de repetición (Parte 78): Un nuevo Chart Trade (V)
En este artículo, veremos cómo deberemos implementar la parte del receptor. Es decir, aquí implementaremos una versión del Asesor Experto, solo para probar y aprender cómo funciona la comunicación vía protocolo. El contenido expuesto aquí tiene un propósito puramente didáctico. En ningún caso debe considerarse una aplicación cuya finalidad no sea el aprendizaje y el estudio de los conceptos mostrados.
Simulador rápido de estrategias comerciales en Python usando Numba Simulador rápido de estrategias comerciales en Python usando Numba
Este artículo implementaremos un simulador rápido de estrategias para modelos de aprendizaje automático utilizando Numba. En cuanto a su velocidad, superará en un factor de 50 a un simulador de estrategias puramente basado en Python. El autor recomienda usar esta biblioteca para acelerar los cálculos matemáticos, y especialmente cuando se utilizan ciclos.
Creación de un Panel de administración de operaciones en MQL5 (Parte III): Ampliación de las clases incorporadas para la gestión de temas (II) Creación de un Panel de administración de operaciones en MQL5 (Parte III): Ampliación de las clases incorporadas para la gestión de temas (II)
En este artículo, ampliaremos cuidadosamente la biblioteca Dialog existente para incorporar la lógica de gestión de temas. Además, integraremos métodos para cambiar de tema en las clases CDialog, CEdit y CButton utilizadas en nuestro proyecto de Panel de administración. Continúe leyendo para obtener perspectivas más reveladoras.
Algoritmo de búsqueda orbital atómica - Atomic Orbital Search (AOS) Algoritmo de búsqueda orbital atómica - Atomic Orbital Search (AOS)
Este artículo analiza el algoritmo AOS (Atomic Orbital Search), que usa conceptos de modelos orbitales atómicos para modelar la búsqueda de soluciones. El algoritmo se basa en distribuciones de probabilidad y en la dinámica de las interacciones en el átomo. El artículo analiza con detalle los aspectos matemáticos del AOS, incluida la actualización de las posiciones de las soluciones candidatas y los mecanismos de absorción y liberación de energía. El AOS descubre nuevos horizontes para la aplicación de los principios cuánticos a los problemas computacionales al ofrecer un enfoque innovador de la optimización.