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