On Tue, 2 Feb 2010, Michael Schuster wrote:
I'm seeking assistance with a rather problematic issue I've been working on for a while, and would welcome all/any thoughts, pointers, RTFMs ... you're willing to share.
Thanks for the very detailed report.Unfortunately (I think?) I can't repeat this in my end on my Debian Linux. I tried this with libcurl 7.19.7 (my stock Debian version).
I edited away the glib-related details from the curltest.c example and when running as instructed I can see the curltest process use the same amount of memory all the time...
I then fired up the curltest program with valgrind --leak-check=full, had it run for a while before I control-c'ed it, and it showed no leaks either!
I'm attaching my edited version of curltest.c to show you what I used. -- / daniel.haxx.se
/* * curltest.c * * repeatedly sends the XML string "heartbeat" (included from curltest.h * because of its size) to http_url or https_url (if using SSL) */ #include <stdio.h> #include <sys/types.h> #include <curl/curl.h> #include <assert.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "curltest.h" /* defines static strings, too long for this file */ #define FALSE 0 #define TRUE 1 typedef char boolean_t; typedef char gchar; /*static char url[] = "https://agent:ag...@127.0.0.1:18443/heartbeat"; */ static char https_url[] = "https://agent:ag...@localhost:18443/heartbeat"; static char http_url[] = "http://agent:ag...@localhost:18080/heartbeat"; static int curl_glob_arg = CURL_GLOBAL_ALL; static boolean_t no_ssl = FALSE; static boolean_t do_cleanup = TRUE; static boolean_t isUrlDefault = TRUE; static char *url = https_url; static char *post_data = heartbeat; static int post_size = sizeof (heartbeat); static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp) { return nmemb * size; } static void timestamp(char *loc, int loc_sz, boolean_t for_xml) { time_t now; struct tm *now_tm; now = time(NULL); if (!for_xml) { now_tm = localtime(&now); sprintf(loc, "%d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", now_tm->tm_year + 1900, now_tm->tm_mon+1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec); } else { now_tm = localtime(&now); /* * this needs to be in sync with <agentUtc>..</agentUtc> string * in curltest.h */ sprintf(loc, "%d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d", now_tm->tm_year + 1900, now_tm->tm_mon+1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec); loc[19] = '.'; /* hack: sprintf overwrites this with \0, which we need to get rid of */ } } static void do_curl_thing(CURL **cu) { CURL *curl = *cu; struct curl_slist *httpheaders = NULL; CURLcode res; gchar *tmp_char; char errbuf[CURL_ERROR_SIZE]; long http_code = 0; boolean_t success = TRUE; char time_str[30]; static time_t last_cleanup = 0; static int consecutive_successes = 0; errbuf[0] = '\0'; if (post_data != NULL) { httpheaders = curl_slist_append(httpheaders, "User-Agent: MySQL Enterprise Agent/"); assert(httpheaders != NULL); tmp_char = "Content-Type: application/xml"; httpheaders = curl_slist_append(httpheaders, tmp_char); /* get current timestamp into xml ... this is a hack */ /* make sure offset is in sync with curltest.h */ timestamp(&post_data[51], 30, TRUE); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_size); } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL); if (no_ssl) { curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_NONE); } else { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_DEFAULT); } curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 4); curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120); curl_easy_setopt(curl, CURLOPT_POST, 1); /* for test, not in MySQL agent */ if (post_data == NULL) curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); res = curl_easy_perform(curl); timestamp(time_str, sizeof (time_str), FALSE); if (CURLE_OK != res) { long curl_errno = 0; #if LIBCURL_VERSION_NUM > 0x070c02 curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &curl_errno); #endif if (res == CURLE_UNSUPPORTED_PROTOCOL) { fprintf(stderr, "%s you requested a unsupported protocol: %s\n", time_str, errbuf); exit(1); } else if (curl_errno != 0) { fprintf(stderr, "%s curl_easy_perform() failed: %s (curl-error = " "'%s' (%u), os-error = '%s' (%ld))\n", time_str, errbuf, curl_easy_strerror(res), res, strerror(curl_errno), curl_errno); } else { fprintf(stderr, "%s curl_easy_perform() failed: %s " "(curl-error = '%s' (%u))\n", time_str, errbuf, curl_easy_strerror(res), res); } success = FALSE; } else if (CURLE_OK != curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &http_code)) { fprintf(stderr, "%s curl_easy_getinfo(CURLINFO_HTTP_CODE) failed " "(code = %u)\n", time_str, res); success = FALSE; } else if (200 != http_code) { if (http_code == 401) { fprintf(stderr, "%s -- received HTTP-status: %ld (failed):" " authentication credentials incorrect\n", time_str, http_code); } else { fprintf(stderr, "%s -- received HTTP-status: %ld (failed)\n", time_str, http_code); } success = FALSE; } curl_slist_free_all(httpheaders); /* * whenever we fail, reset curl connection in the hope of * keeping mem footprint low. * every 5 minutes or more, reset whole curl state */ if (FALSE == success) { consecutive_successes = 0; if (do_cleanup == FALSE) return; time_t now; curl_easy_cleanup(curl); now = time(NULL); if (last_cleanup == 0) { last_cleanup = now; } else if (now - last_cleanup > (5 * 60)) { curl_global_cleanup(); if (curl_global_init(curl_glob_arg) != 0) { fprintf(stderr, "periodic re-global-init failed\n"); exit(2); } } *cu = curl_easy_init(); } if (success && (consecutive_successes++ % 10 == 0)) printf("%s successfully reconnected to %s\n", time_str, url); } CURL *curl; static void cleanup(int sig) { curl_easy_cleanup(curl); curl_global_cleanup(); exit(0); } static void Usage(char *name) { fprintf(stderr, "Usage: %s [-scr] [-u url]\n" "\t-s ... no ssl (default: use ssl)\n" "\t-c ... no curl cleanup after error\n" "\t-p ... no post data (get only)\n" "\t-r ... use \"resync\" xml message instead of longer heartbeat\n" "\t-u url ... use url instead of default\n", name); exit(1); } int main(int argc, char **argv) { int c; while ((c = getopt(argc, argv, "spcru:")) != -1) { switch (c) { case 'u': url = optarg; isUrlDefault = FALSE; break; case 's': no_ssl = TRUE; curl_glob_arg = CURL_GLOBAL_NOTHING; /* only change url if it's not in argv (see -u) */ if (url == https_url) url = http_url; break; case 'p': post_data = NULL; break; case 'r': post_data = xml_resync; post_size = sizeof (xml_resync); break; case 'c': do_cleanup = FALSE; break; default: Usage(argv[0]); /* notreached */ break; } } if (curl_global_init(curl_glob_arg) != 0) { fprintf(stderr, "cannot curl_global_init()\n"); exit(1); } curl = curl_easy_init(); assert(curl != NULL); for (;;) { do_curl_thing(&curl); sleep(5); } }
------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.html