/*
    gcc curl_nss_test_case.c -pthread -lcurl
*/

#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <curl/curl.h>

#define OPENSSL 0
#if OPENSSL
static void openssl_init_locks(void);
#endif

#define URL         "https://lucy.localdomain/test.png"
#define CACERT      "test.pem"
#define NUM_THREADS 32

static sem_t ready;
static sem_t go;

static size_t dummy_write(void *buffer, size_t size, size_t nmemb, void *userp)
{
    return size * nmemb;
}

static void *thread_func(void *arg)
{
    CURL *curl;
    CURLcode status;

    curl = curl_easy_init();
    if (curl == 0)
        exit(2);

    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
    curl_easy_setopt(curl, CURLOPT_URL, URL);
    curl_easy_setopt(curl, CURLOPT_CAINFO, CACERT);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dummy_write);
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

    sem_post(&ready);
    sem_wait(&go);

    status = curl_easy_perform(curl);

    if (status != CURLE_OK) {
        fprintf(stderr, "error %d: %s\n", status, curl_easy_strerror(status));
    }

    curl_easy_cleanup(curl);

    return NULL;
}

int main(void)
{
    pthread_t threads[NUM_THREADS];
    int i;
    int rc;

    printf("%s\n", curl_version());

    curl_global_init(CURL_GLOBAL_ALL);

#if OPENSSL
    openssl_init_locks();
#endif

    sem_init(&ready, 0, 0);
    sem_init(&go, 0, 0);

    for (i = 0; i < NUM_THREADS; i++) {
        rc = pthread_create(&threads[i], NULL, thread_func, NULL);
        if (rc != 0)
            return 1;
    }

    for (i = 0; i < NUM_THREADS; i++) {
        sem_wait(&ready);
    }

    for (i = 0; i < NUM_THREADS; i++) {
        sem_post(&go);
    }

    for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }

    return 0;
}

/* The following is just for comparison with OpenSSL. */

#if OPENSSL
#include <openssl/crypto.h>

static pthread_mutex_t *openssl_lock_array;

static void openssl_lock_callback(int mode, int type, const char *file, int line)
{
    int rc;

    if (mode & CRYPTO_LOCK) {
        rc = pthread_mutex_lock(&openssl_lock_array[type]);
    } else {
        rc = pthread_mutex_unlock(&openssl_lock_array[type]);
    }
    if (rc != 0) {
        abort();
    }
}

static unsigned long openssl_thread_id(void)
{
    return (unsigned long) pthread_self();
}

static void openssl_init_locks(void)
{
    int i;
    int rc;

    openssl_lock_array = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
    for (i = 0; i < CRYPTO_num_locks(); i++) {
        rc = pthread_mutex_init(&openssl_lock_array[i], NULL);
        if (rc != 0) {
            abort();
        }
    }

    CRYPTO_set_id_callback(openssl_thread_id);
    CRYPTO_set_locking_callback(openssl_lock_callback);
}
#endif

