CURLOPT_WRITEFUNCTION y CURLOPT_WRITEDATA para libcurl en C

En este artículo voy a hablar sobre la opción CURLOPT_WRITEFUNCTION que existe al usar libcurl en el lenguaje de programación C.

Esta función se encargará de recibir los datos, pero tiene una particularidad y es que dicha función podría ser llamada al menos 1 vez, hasta una cantidad de veces indefinidas. En cada llamada se le pasará una cierta cantidad de datos, a veces pueden ser todos, a veces solo una parte, y nosotros debemos estar preparados para recibirlos y almacenarlos en algún lugar.

Por cierto, el callback por defecto en esta opción es fwrite que simplemente imprime los datos.

Yo te mostraré un ejemplo para almacenar todos los datos en una cadena dinámica, ya que al tener una cadena dinámica vas a poder compararla, imprimirla o almacenarla en algún lugar.

Cadena dinámica

No siempre vamos a necesitar los datos como cadena. Puede que al descargar un archivo binario simplemente necesitemos escribir los bytes así como los vamos recibiendo, sin embargo en mi caso particular necesito leer la respuesta HTTP como una cadena así que implementé mi propia estructura de datos para tener una cadena dinámica.

Aquí voy a usar ese struct pero no explicaré su funcionamiento, ya que eso está en su propio post: implementación de cadena dinámica en C.

Iniciando datos

Comenzamos inicializando la cadena vacía:

struct Cadena *miCadena = (struct Cadena *)malloc(sizeof(struct Cadena));
if (miCadena == NULL)
{
	printf("Sin memoria suficiente para alojar el struct de cadena");
	return EXIT_FAILURE;
}
iniciarCadena(miCadena);

Y ahora ya podemos concatenar sin límite:

void concatenar(struct Cadena *cadena, char *otraCadena)
{
	size_t longitudDeOtraCadena = strlen(otraCadena);
	size_t nuevaLongitud = cadena->longitud + longitudDeOtraCadena;
	size_t nuevaLongitudConByteNulo = nuevaLongitud + 1;
	size_t longitudDeOtraCadenaIncluyendoByteNulo = longitudDeOtraCadena + 1;
	char *cadenaConNuevaLongitud = realloc(cadena->datos, nuevaLongitudConByteNulo);
	if (cadenaConNuevaLongitud == NULL)
	{
		printf("Sin memoria suficiente para concatenar la nueva cadena");
		return;
	}
	cadena->datos = cadenaConNuevaLongitud;
	memcpy((char *)(cadena->datos + cadena->longitud), otraCadena, longitudDeOtraCadenaIncluyendoByteNulo);
	cadena->longitud = nuevaLongitud;
}

Función de escritura

Después definimos la función write_callback que recibe el fragmento de datos y concatenamos los datos a la cadena, asumiendo que los datos devueltos son de tipo cadena:

size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{
	// Esta función podría ser invocada varias veces por cURL. https://curl.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
	size_t realsize = size * nmemb;
	concatenar((struct Cadena *)userp, (char *)contents);
	concatenar((struct Cadena *)userp, "");
	return realsize;
}

Finalmente indicamos dicha función en CURLOPT_WRITEFUNCTION y pasamos el apuntador a dicha cadena en CURLOPT_WRITEDATA así:

struct Cadena *miCadena = (struct Cadena *)malloc(sizeof(struct Cadena));
if (miCadena == NULL)
{
	printf("Sin memoria suficiente para alojar el struct de cadena");
	return EXIT_FAILURE;
}
iniciarCadena(miCadena);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)miCadena);

De este modo, la respuesta será almacenada en la estructura de datos de la cadena dinámica. Es un ejemplo simple y sencillo sobre cómo usar CURLOPT_WRITEDATA – pointer passed to the write callback y CURLOPT_WRITEFUNCTION – callback for writing received data.

Más adelante te mostraré el ejemplo de una petición GET y POST que utilizan esta función para recibir los datos, pero era necesario colocar por separado esa función ya que aplica lo mismo para todos los tipos de petición.

Actualización

Ya están publicados los artículos con ejemplos para hacer una petición POST con C y para hacer una petición GET con ANSI C igualmente.

Encantado de ayudarte


Estoy disponible para trabajar en tu proyecto, modificar el programa del post o realizar tu tarea pendiente, no dudes en ponerte en contacto conmigo.

No te pierdas ninguno de mis posts

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *