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.