xref: /curl/tests/libtest/lib3207.c (revision 71cf0d1f)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 #include "test.h"
25 #include "testutil.h"
26 #include "memdebug.h"
27 
28 #include <stdio.h>
29 
30 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
31 #if defined(USE_THREADS_POSIX)
32 #include <pthread.h>
33 #endif
34 #include "curl_threads.h"
35 #endif
36 
37 #define CAINFO libtest_arg2
38 #define THREAD_SIZE 16
39 #define PER_THREAD_SIZE 8
40 
41 struct Ctx {
42   const char *URL;
43   CURLSH *share;
44   int result;
45   int thread_id;
46   struct curl_slist *contents;
47 };
48 
write_memory_callback(void * contents,size_t size,size_t nmemb,void * userp)49 static size_t write_memory_callback(void *contents, size_t size,
50   size_t nmemb, void *userp) {
51     /* append the data to contents */
52     size_t realsize = size * nmemb;
53     struct Ctx *mem = (struct Ctx *)userp;
54     char *data = (char *)malloc(realsize + 1);
55     struct curl_slist *item_append = NULL;
56     if(!data) {
57       printf("not enough memory (malloc returned NULL)\n");
58       return 0;
59     }
60     memcpy(data, contents, realsize);
61     data[realsize] = '\0';
62     item_append = curl_slist_append(mem->contents, data);
63     free(data);
64     if(item_append) {
65       mem->contents = item_append;
66     }
67     else {
68       printf("not enough memory (curl_slist_append returned NULL)\n");
69       return 0;
70     }
71     return realsize;
72 }
73 
74 static
75 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
76 #if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
77 DWORD
78 #else
79 unsigned int
80 #endif
81 CURL_STDCALL
82 #else
83 unsigned int
84 #endif
test_thread(void * ptr)85 test_thread(void *ptr)
86 {
87   struct Ctx *ctx = (struct Ctx *)ptr;
88   CURLcode res = CURLE_OK;
89 
90   int i;
91 
92   /* Loop the transfer and cleanup the handle properly every lap. This will
93      still reuse ssl session since the pool is in the shared object! */
94   for(i = 0; i < PER_THREAD_SIZE; i++) {
95     CURL *curl = curl_easy_init();
96     if(curl) {
97       curl_easy_setopt(curl, CURLOPT_URL, (char *)ctx->URL);
98 
99       /* use the share object */
100       curl_easy_setopt(curl, CURLOPT_SHARE, ctx->share);
101       curl_easy_setopt(curl, CURLOPT_CAINFO, CAINFO);
102 
103       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback);
104       curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
105       curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
106 
107       /* Perform the request, res will get the return code */
108       res = curl_easy_perform(curl);
109 
110       /* always cleanup */
111       curl_easy_cleanup(curl);
112       /* Check for errors */
113       if(res != CURLE_OK) {
114         fprintf(stderr, "curl_easy_perform() failed: %s\n",
115                 curl_easy_strerror(res));
116         goto test_cleanup;
117       }
118     }
119   }
120 
121 test_cleanup:
122   ctx->result = (int)res;
123   return 0;
124 }
125 
126 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
127 
test_lock(CURL * handle,curl_lock_data data,curl_lock_access laccess,void * useptr)128 static void test_lock(CURL *handle, curl_lock_data data,
129                       curl_lock_access laccess, void *useptr)
130 {
131   curl_mutex_t *mutexes = (curl_mutex_t*) useptr;
132   (void)handle;
133   (void)laccess;
134   Curl_mutex_acquire(&mutexes[data]);
135 }
136 
test_unlock(CURL * handle,curl_lock_data data,void * useptr)137 static void test_unlock(CURL *handle, curl_lock_data data, void *useptr)
138 {
139   curl_mutex_t *mutexes = (curl_mutex_t*) useptr;
140   (void)handle;
141   Curl_mutex_release(&mutexes[data]);
142 }
143 
execute(CURLSH * share,struct Ctx * ctx)144 static void execute(CURLSH *share, struct Ctx *ctx)
145 {
146   int i;
147   curl_mutex_t mutexes[CURL_LOCK_DATA_LAST - 1];
148   curl_thread_t thread[THREAD_SIZE];
149   for(i = 0; i < CURL_LOCK_DATA_LAST - 1; i++) {
150     Curl_mutex_init(&mutexes[i]);
151   }
152   curl_share_setopt(share, CURLSHOPT_LOCKFUNC, test_lock);
153   curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, test_unlock);
154   curl_share_setopt(share, CURLSHOPT_USERDATA, (void *)mutexes);
155   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
156 
157   for(i = 0; i < THREAD_SIZE; i++) {
158     thread[i] = Curl_thread_create(test_thread, (void *)&ctx[i]);
159   }
160   for(i = 0; i < THREAD_SIZE; i++) {
161     if(thread[i]) {
162       Curl_thread_join(&thread[i]);
163       Curl_thread_destroy(thread[i]);
164     }
165   }
166   curl_share_setopt(share, CURLSHOPT_LOCKFUNC, NULL);
167   curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, NULL);
168   for(i = 0; i < CURL_LOCK_DATA_LAST - 1; i++) {
169     Curl_mutex_destroy(&mutexes[i]);
170   }
171 }
172 
173 #else /* without pthread, run serially */
174 
execute(CURLSH * share,struct Ctx * ctx)175 static void execute(CURLSH *share, struct Ctx *ctx)
176 {
177   int i;
178   (void) share;
179   for(i = 0; i < THREAD_SIZE; i++) {
180     test_thread((void *)&ctx[i]);
181   }
182 }
183 
184 #endif
185 
test(char * URL)186 CURLcode test(char *URL)
187 {
188   int res = 0;
189   int i;
190   CURLSH* share;
191   struct Ctx ctx[THREAD_SIZE];
192 
193   curl_global_init(CURL_GLOBAL_ALL);
194 
195   share = curl_share_init();
196   if(!share) {
197     fprintf(stderr, "curl_share_init() failed\n");
198     goto test_cleanup;
199   }
200 
201   for(i = 0; i < THREAD_SIZE; i++) {
202     ctx[i].share = share;
203     ctx[i].URL = URL;
204     ctx[i].thread_id = i;
205     ctx[i].result = 0;
206     ctx[i].contents = NULL;
207   }
208 
209   execute(share, ctx);
210 
211   for(i = 0; i < THREAD_SIZE; i++) {
212     if(ctx[i].result) {
213       res = ctx[i].result;
214     }
215     else {
216       struct curl_slist *item = ctx[i].contents;
217       while(item) {
218         printf("%s", item->data);
219         item = item->next;
220       }
221     }
222     curl_slist_free_all(ctx[i].contents);
223   }
224 
225 test_cleanup:
226   if(share)
227     curl_share_cleanup(share);
228   curl_global_cleanup();
229   return (CURLcode)res;
230 }
231