"Your example is not complete, but based on what you have provided here is what you should look at:"
Yes - as I said, pseudo code. It was meant to give you an idea.

"Strings passed to libcurl as 'char *' arguments, are copied by the library" [1] with the exception of CURLOPT_POSTFIELDS."
This is what I needed to know. Thanks!

"If your program is crashing when you delete the buffer after setting it as the URL then the problem is elsewhere."
Program is not crashing. Try it.

"Handles. You must never share the same handle in multiple threads. You can pass the handles around among threads, but you must never use a single handle from more than one thread at any given time." I'm not sharing handles. Take a look at ServerProc. That's the thread responsible for multi_* interface. It's the only thread operating on hMultiHandle without protection. While other threads that uses PushRequest are locking the section where they use hMultiHandle. If libcurl is thread safe then my concept is fine.

"Also, I notice you didn't give your curl_version() information. libcurl is continuously improving, and so is nghttp2. I would try the latest of both."

7.51.0 - DEV
openssl - 1.0.2h
nghttp2 1.15.0

As I said - I have no problems. Program is working fine. After I added "connclose" in close_all_connections as suggested at the begging of this email chain - everything worked fine.

Though I was worried that maybe I haven't implemented:
multi_perform
multi_wait
multi_read

in a way they were intended. That's all.
Try the program (in the attachment) and we'll continue discussion.

--
Miloš Ljumović
Operating systems specialist Spec.App.
http://milos.expert.its.me

#define HAVE_BOOL_T

#ifndef __CURL_INCLUDED__
#define __CURL_INCLUDED__
#include <curl/curl.h>
#include <hash.h>
#include <multihandle.h>
#endif

#define PROXY_URL                       "YOUR PROXY URL:PORT GOES HERE"
#define PROXY_CREDENTIALS       "YOUR PROXY USER:PASS GOES HERE"

#define CERT_PATH                       "YOUR CERTIFICATE PATH GOES HERE"
#define CERT_KEYPATH            "YOUR CERTIFICATE KEY PATH GOES HERE"
#define CERT_CAPATH                     "YOUR CERTIFICATE CA PATH GOES HERE"
#define CERT_PASSWORD           "YOUR CERTIFICATE PASSWORD GOES HERE"

#define APNS_TOPIC                      "YOUR APNS TOPIC GOES HERE"

#include <Windows.h>

class CTransfers
{
public:
        CTransfers(void) : lTransfers(0)
        {
                hEvent = CreateEvent(0, TRUE, FALSE, 
"__12345_PUSHTRANSFER_67890__");
        }

        ~CTransfers(void)
        {
                SetEvent(hEvent);
                WaitForSingleObject(hEvent, 10);
                CloseHandle(hEvent);
        }

        long operator++(int)
        {
                InterlockedIncrement(&lTransfers);
                return Get();
        }

        long operator--(int)
        {
                InterlockedDecrement(&lTransfers);
                return Get();
        }

        long operator=(long lValue)
        {
                InterlockedExchange(&lTransfers, lValue);
                return Get();
        }

        long Get(void)
        {
                long lResult = InterlockedExchangeAdd(&lTransfers, 0);
                if (lResult == 0)
                {
                        ResetEvent(hEvent);
                }
                else if (lResult == 1)
                {
                        SetEvent(hEvent);
                }
                return lResult;
        }

        void Pulse(void)
        {
                SetEvent(hEvent);
        }

        void WaitInput(void)
        {
                WaitForSingleObject(hEvent, INFINITE);
        }
private:
        HANDLE hEvent;
        long lTransfers;
};

typedef long (__cdecl* RESPONSECALLBACK)(long lResponseCode, char* 
szDeviceToken);

class CPushResponse
{
public:
        CPushResponse(long lResponseCode, char* szDeviceToken)
        {
                ResponseCode = lResponseCode;
                char* pszDeviceToken = strrchr(szDeviceToken, '/');
                if (pszDeviceToken)
                {
                        strcpy(DeviceToken, ++pszDeviceToken);
                }
                else
                {
                        strcpy(DeviceToken, szDeviceToken);
                }
        }
public:
        long ResponseCode;
        char DeviceToken[128];
};

class CPushService
{
public:
        CPushService(RESPONSECALLBACK fnResponseCallback) : 
ResponseCallback(fnResponseCallback)
        {
                // 
                SecureZeroMemory(szOutputBuffer, 4096);
                
                // MD5 hash: [__SERVERREADY_20161015180622331__]
                pszReadyEvent = "362c4945d1c91cb8563d7678a5f09930";
                // MD5 hash: [__SERVEREXIT_20161016114452997__]
                pszExitEvent = "fe9ff286fcb8f9c19522b12b6f05677a";
                // MD5 hash: [__DISPATCHEREVENT_20161017041344443__]
                pszDispatcherEvent = "f8eb63cc78ae0fd950021662f5aca3cd";
                
                // 
                InitializeLocks();

                // 
                hMultiHandle = curl_multi_init();
                if (hMultiHandle)
                {
                        int iTransfers = 0;
                        int iStillRunning = 0;
                        hMulti = static_cast<Curl_multi*>(hMultiHandle);
                        // 
                        curl_multi_setopt(hMultiHandle, CURLMOPT_PIPELINING, 
CURLPIPE_MULTIPLEX);
                        // 
                        curl_multi_setopt(hMultiHandle, CURLMOPT_PUSHDATA, 
&iTransfers);
                        // 
                        curl_multi_perform(hMultiHandle, &iStillRunning);
                }

                // 
                hServerReady = CreateEvent(0, TRUE, FALSE, pszReadyEvent);
                //
                hServer = CreateThread(0, 0, ServerProc, this, 0, &dwServerID);
                //
                WaitForSingleObject(hServerReady, 60 * 1000);

                //
                hDispatcherReady = CreateEvent(0, TRUE, FALSE, 
pszDispatcherEvent);
                // 
                hDispatcher = CreateThread(0, 0, DispatcherProc, this, 0, 
&dwDispatcherID);
                //
                WaitForSingleObject(hDispatcherReady, 60 * 1000);

                //
                hExit = CreateEvent(0, TRUE, FALSE, pszExitEvent);
        }

        virtual ~CPushService(void)
        {
                //
                transfers.Pulse();

                //
                SetEvent(hExit);
                //
                WaitForSingleObject(hServer, 60 * 1000);
                //
                CloseHandle(hServer);
                //
                CloseHandle(hServerReady);
                //
                CloseHandle(hExit);

                //
                if (hMultiHandle)
                {
                        //
                        curl_multi_cleanup(hMultiHandle);
                }

                //
                ReleaseLocks();

                //
                PostThreadMessage(dwDispatcherID, MSG_EXITSERVER, 0, 0);
                //
                WaitForSingleObject(hDispatcher, 60 * 1000);
                //
                CloseHandle(hDispatcher);
                // 
                CloseHandle(hDispatcherReady);
        }

        long PushRequest(char* szDeviceToken, char* szContent, bool bVerbose = 
true)
        {
                CURL* hEasyHandle = 0;
                Lock();
                {
                        hEasyHandle = curl_easy_init();
                }
                Unlock();

                if (!hEasyHandle)
                {
                        return -1;
                }

                curl_easy_setopt(hEasyHandle, CURLOPT_PROXY, PROXY_URL);
                curl_easy_setopt(hEasyHandle, CURLOPT_PROXYUSERPWD, 
PROXY_CREDENTIALS);
        
                sprintf(szUrlBuffer, "https://api.push.apple.com/3/device/%s";, 
szDeviceToken);
                curl_easy_setopt(hEasyHandle, CURLOPT_URL, szUrlBuffer);
                if (bVerbose)
                {
                        curl_easy_setopt(hEasyHandle, CURLOPT_VERBOSE, 1L);
                }
                curl_easy_setopt(hEasyHandle, CURLOPT_HTTP_VERSION, 
CURL_HTTP_VERSION_2_0);
                
                char* szContentBuffer = new char[4096]; /// FREE MEMORY LATER
                sprintf(szContentBuffer, 
"{\"aps\":{\"alert\":\"%s\",\"sound\":\"default\"}}", szContent);
                __int64 size = (__int64)strlen(szContentBuffer);
                curl_easy_setopt(hEasyHandle, CURLOPT_POSTFIELDS, 
szContentBuffer);
                curl_easy_setopt(hEasyHandle, CURLOPT_POSTFIELDSIZE_LARGE, 
size);

                curl_easy_setopt(hEasyHandle, CURLOPT_SSLCERTTYPE, 0);
                curl_easy_setopt(hEasyHandle, CURLOPT_SSLKEYTYPE, 0);
                
                curl_easy_setopt(hEasyHandle, CURLOPT_SSLCERT, CERT_PATH);
                curl_easy_setopt(hEasyHandle, CURLOPT_SSLKEY, CERT_KEYPATH);
                curl_easy_setopt(hEasyHandle, CURLOPT_KEYPASSWD, CERT_PASSWORD);
                curl_easy_setopt(hEasyHandle, CURLOPT_CAINFO, CERT_CAPATH);
                curl_easy_setopt(hEasyHandle, CURLOPT_SSL_VERIFYPEER, 0L);
                curl_easy_setopt(hEasyHandle, CURLOPT_SSL_VERIFYHOST, 0L);

                curl_easy_setopt(hEasyHandle, CURLOPT_AUTOREFERER, 0L);
                curl_easy_setopt(hEasyHandle, CURLOPT_HEADER, 0L);
                curl_easy_setopt(hEasyHandle, CURLOPT_USERAGENT, 
"curl/7.51.0-DEV");

                curl_slist *chunk = NULL;
                char szTopicBuffer[1024] = { 0 };
                sprintf(szTopicBuffer, "apns-topic: %s", APNS_TOPIC);
                chunk = curl_slist_append(chunk, szTopicBuffer);
                curl_easy_setopt(hEasyHandle, CURLOPT_HTTPHEADER, chunk);

                curl_easy_setopt(hEasyHandle, CURLOPT_TRANSFERTEXT, 0L);
                curl_easy_setopt(hEasyHandle, CURLOPT_TIMEOUT_MS, 0);
                curl_easy_setopt(hEasyHandle, CURLOPT_MAXREDIRS, 50);
                curl_easy_setopt(hEasyHandle, CURLOPT_POSTREDIR, 0);
                curl_easy_setopt(hEasyHandle, CURLOPT_RESUME_FROM_LARGE, 
CURL_OFF_T_C(0));
                curl_easy_setopt(hEasyHandle, CURLOPT_TCP_KEEPALIVE, 1L);

        #if (CURLPIPE_MULTIPLEX > 0)
                /* wait for pipe connection to confirm */ 
                curl_easy_setopt(hEasyHandle, CURLOPT_PIPEWAIT, 1L);
        #endif
        
                AddHandle(hMultiHandle, hEasyHandle);
                return 0L;
        }
protected:
        void InitializeLocks(void)
        {
                InitializeCriticalSection(&lock);
        }

        void ReleaseLocks(void)
        {
                DeleteCriticalSection(&lock);
        }

        void Lock(void)
        {
                EnterCriticalSection(&lock);
        }

        void Unlock(void)
        {
                LeaveCriticalSection(&lock);
        }

        CURLMcode AddHandle(CURLM* hMultiHandle, CURL* hEasyHandle)
        {
                CURLMcode result = CURLM_OK;
                size_t uTransfers = 0;
                
                Lock();
                {
                        result = curl_multi_add_handle(hMultiHandle, 
hEasyHandle);
                }
                Unlock();
                
                transfers++;
                return result;
        }

        void RemoveHandle(CURLM* hMultiHandle, CURL* hEasyHandle)
        {
                Lock();
                {
                        curl_multi_remove_handle(hMultiHandle, hEasyHandle);
                }
                Unlock();

                transfers--;
        }

        void ReleaseHandle(CURL* hEasyHandle)
        {
                curl_easy_cleanup(hEasyHandle);
        }

        DWORD Running(void)
        {
                return WaitForSingleObject(hExit, 0);
        }

        CURLMcode Perform(void)
        {
                int numfds = 0;
                int iStillRunning = 0;
                CURLMcode result = CURLM_OK;
                
                if ((result = curl_multi_perform(hMultiHandle, &iStillRunning)) 
== CURLM_OK)
                {
                        result = curl_multi_wait(hMultiHandle, NULL, 0, 100, 
&numfds);
                }

                return result;
        }

        CURLMsg* Read(void)
        {
                int msgq = 0;
                return curl_multi_info_read(hMultiHandle, &msgq);
        }

        static DWORD WINAPI ServerProc(LPVOID hParameter)
        {
                CPushService* pThis = static_cast<CPushService*>(hParameter);
                CURLMsg* hMsg = 0;

                HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, 
pThis->pszReadyEvent);
                SetEvent(hEvent);
                CloseHandle(hEvent);

                long response = 0;
                char* buffer = 0;
                CURLMcode mcResult = CURLM_OK;
                while (pThis->Running())
                {
                        pThis->transfers.WaitInput();
                        pThis->Perform();
                
                        do
                        {
                                hMsg = pThis->Read();
                                if (hMsg && (hMsg->msg == CURLMSG_DONE))
                                {
                                        curl_easy_getinfo(hMsg->easy_handle, 
CURLINFO_RESPONSE_CODE, &response);
                                        curl_easy_getinfo(hMsg->easy_handle, 
CURLINFO_EFFECTIVE_URL, &buffer);
                                        
                                        CPushResponse* reponse = new 
CPushResponse(response, buffer);
                                        
PostThreadMessage(pThis->dwDispatcherID, MSG_NEWREQUEST, 0, 
reinterpret_cast<LPARAM>(reponse));
                                
                                        
pThis->RemoveHandle(pThis->hMultiHandle, hMsg->easy_handle);
                                        pThis->ReleaseHandle(hMsg->easy_handle);
                                }
                        } while(hMsg);
                }
                return 0L;
        }

        static DWORD WINAPI DispatcherProc(LPVOID hParameter)
        {
                CPushService* pThis = static_cast<CPushService*>(hParameter);

                MSG msg = { 0 };
                PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);

                HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, 
pThis->pszDispatcherEvent);
                SetEvent(hEvent);
                CloseHandle(hEvent);

                while (GetMessage(&msg, 0, MSG_EXITSERVER, MSG_NEWREQUEST))
                {
                        switch (msg.message)
                        {
                        case MSG_EXITSERVER:
                                return 0L;
                        case MSG_NEWREQUEST:
                                CPushResponse* response = 
reinterpret_cast<CPushResponse*>(msg.lParam);
                                pThis->ResponseCallback(response->ResponseCode, 
response->DeviceToken);
                                delete response;
                                break;
                        }
                }

                return 0L;
        }
private:
        CURLM* hMultiHandle;
        Curl_multi* hMulti;
private:
        CRITICAL_SECTION lock;
        CTransfers transfers;
private:
        char* pszExitEvent;
        HANDLE hExit;
private:
        char* pszReadyEvent;
        HANDLE hServerReady;
        HANDLE hServer;
        DWORD dwServerID;
private:
        char* pszDispatcherEvent;
        HANDLE hDispatcherReady;
        HANDLE hDispatcher;
        DWORD dwDispatcherID;
        RESPONSECALLBACK ResponseCallback;
private:
        static const unsigned MSG_EXITSERVER = 0x0400;
        static const unsigned MSG_NEWREQUEST = 0x0401;

private:
        size_t NumberTransfers(void)
        {
                return hMulti->conn_cache.num_connections;
        }       
};

long Callback(long lResponseCode, char* szDeviceToken)
{
        // DO PROCESSING
        // SAVE RESPONSE CODE, ...
        // 
        switch (lResponseCode)
        {
        case 200:
                // success
                break;
        case 400:
                // bad request
                break;
        /* ... */
        }
        return 0L;
}

CPushService* g_PushService = 0;

DWORD WINAPI ThreadStart(LPVOID hParameter)
{
        char* szDeviceToken = static_cast<char*>(hParameter);
        g_PushService->PushRequest(sDeviceToken, "Hello");
        return 0L;
}

#define NUM_TOKENS      SET_THE_NUMBER_OF_TOKENS_HERE
#define NUM_THREADS     100

int main(void)
{
        char* pszDeviceTokens[NUM_TOKENS] = {
                "place some tokens here",
                "place some tokens here",
                /* ... */
        };
        g_PushService = new CPushService(Callback);

        HANDLE hThreads[NUM_THREADS] = { 0 };
        for (int i = 0; i < NUM_THREADS; i++)
        {
                hThreads[i] = CreateThread(0, 0, ThreadStart, 
pszDeviceTokens[rand() % NUM_TOKENS], 0, 0);
        }

        DWORD dwStatus = WaitForMultipleObjects(NUM_THREADS, hThreads, TRUE, 
1000 * 60 * 10);
        if (dwStatus != WAIT_OBJECT_0)
        {
                // something went wrong
        }

        // Give it some time to finish
        Sleep(120 * 1000);

        delete g_PushService;
        return 0;
}
-------------------------------------------------------------------
List admin: https://cool.haxx.se/list/listinfo/curl-library
Etiquette:  https://curl.haxx.se/mail/etiquette.html

Reply via email to