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(char * contents,size_t size,size_t nmemb,void * userp)49 static size_t write_memory_callback(char *contents, size_t size,
50 size_t nmemb, void *userp)
51 {
52 /* append the data to contents */
53 size_t realsize = size * nmemb;
54 struct Ctx *mem = (struct Ctx *)userp;
55 char *data = (char *)malloc(realsize + 1);
56 struct curl_slist *item_append = NULL;
57 if(!data) {
58 printf("not enough memory (malloc returned NULL)\n");
59 return 0;
60 }
61 memcpy(data, contents, realsize);
62 data[realsize] = '\0';
63 item_append = curl_slist_append(mem->contents, data);
64 free(data);
65 if(item_append) {
66 mem->contents = item_append;
67 }
68 else {
69 printf("not enough memory (curl_slist_append returned NULL)\n");
70 return 0;
71 }
72 return realsize;
73 }
74
75 static
76 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
77 #if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
78 DWORD
79 #else
80 unsigned int
81 #endif
82 CURL_STDCALL
83 #else
84 unsigned int
85 #endif
test_thread(void * ptr)86 test_thread(void *ptr)
87 {
88 struct Ctx *ctx = (struct Ctx *)ptr;
89 CURLcode res = CURLE_OK;
90
91 int i;
92
93 /* Loop the transfer and cleanup the handle properly every lap. This will
94 still reuse ssl session since the pool is in the shared object! */
95 for(i = 0; i < PER_THREAD_SIZE; i++) {
96 CURL *curl = curl_easy_init();
97 if(curl) {
98 curl_easy_setopt(curl, CURLOPT_URL, (char *)ctx->URL);
99
100 /* use the share object */
101 curl_easy_setopt(curl, CURLOPT_SHARE, ctx->share);
102 curl_easy_setopt(curl, CURLOPT_CAINFO, CAINFO);
103
104 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback);
105 curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
106 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
107
108 /* Perform the request, res will get the return code */
109 res = curl_easy_perform(curl);
110
111 /* always cleanup */
112 curl_easy_cleanup(curl);
113 /* Check for errors */
114 if(res != CURLE_OK) {
115 fprintf(stderr, "curl_easy_perform() failed: %s\n",
116 curl_easy_strerror(res));
117 goto test_cleanup;
118 }
119 }
120 }
121
122 test_cleanup:
123 ctx->result = (int)res;
124 return 0;
125 }
126
127 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
128
test_lock(CURL * handle,curl_lock_data data,curl_lock_access laccess,void * useptr)129 static void test_lock(CURL *handle, curl_lock_data data,
130 curl_lock_access laccess, void *useptr)
131 {
132 curl_mutex_t *mutexes = (curl_mutex_t*) useptr;
133 (void)handle;
134 (void)laccess;
135 Curl_mutex_acquire(&mutexes[data]);
136 }
137
test_unlock(CURL * handle,curl_lock_data data,void * useptr)138 static void test_unlock(CURL *handle, curl_lock_data data, void *useptr)
139 {
140 curl_mutex_t *mutexes = (curl_mutex_t*) useptr;
141 (void)handle;
142 Curl_mutex_release(&mutexes[data]);
143 }
144
execute(CURLSH * share,struct Ctx * ctx)145 static void execute(CURLSH *share, struct Ctx *ctx)
146 {
147 int i;
148 curl_mutex_t mutexes[CURL_LOCK_DATA_LAST - 1];
149 curl_thread_t thread[THREAD_SIZE];
150 for(i = 0; i < CURL_LOCK_DATA_LAST - 1; i++) {
151 Curl_mutex_init(&mutexes[i]);
152 }
153 curl_share_setopt(share, CURLSHOPT_LOCKFUNC, test_lock);
154 curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, test_unlock);
155 curl_share_setopt(share, CURLSHOPT_USERDATA, (void *)mutexes);
156 curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
157
158 for(i = 0; i < THREAD_SIZE; i++) {
159 thread[i] = Curl_thread_create(test_thread, (void *)&ctx[i]);
160 }
161 for(i = 0; i < THREAD_SIZE; i++) {
162 if(thread[i]) {
163 Curl_thread_join(&thread[i]);
164 Curl_thread_destroy(thread[i]);
165 }
166 }
167 curl_share_setopt(share, CURLSHOPT_LOCKFUNC, NULL);
168 curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, NULL);
169 for(i = 0; i < CURL_LOCK_DATA_LAST - 1; i++) {
170 Curl_mutex_destroy(&mutexes[i]);
171 }
172 }
173
174 #else /* without pthread, run serially */
175
execute(CURLSH * share,struct Ctx * ctx)176 static void execute(CURLSH *share, struct Ctx *ctx)
177 {
178 int i;
179 (void) share;
180 for(i = 0; i < THREAD_SIZE; i++) {
181 test_thread((void *)&ctx[i]);
182 }
183 }
184
185 #endif
186
test(char * URL)187 CURLcode test(char *URL)
188 {
189 int res = 0;
190 int i;
191 CURLSH* share;
192 struct Ctx ctx[THREAD_SIZE];
193
194 curl_global_init(CURL_GLOBAL_ALL);
195
196 share = curl_share_init();
197 if(!share) {
198 fprintf(stderr, "curl_share_init() failed\n");
199 goto test_cleanup;
200 }
201
202 for(i = 0; i < THREAD_SIZE; i++) {
203 ctx[i].share = share;
204 ctx[i].URL = URL;
205 ctx[i].thread_id = i;
206 ctx[i].result = 0;
207 ctx[i].contents = NULL;
208 }
209
210 execute(share, ctx);
211
212 for(i = 0; i < THREAD_SIZE; i++) {
213 if(ctx[i].result) {
214 res = ctx[i].result;
215 }
216 else {
217 struct curl_slist *item = ctx[i].contents;
218 while(item) {
219 printf("%s", item->data);
220 item = item->next;
221 }
222 }
223 curl_slist_free_all(ctx[i].contents);
224 }
225
226 test_cleanup:
227 if(share)
228 curl_share_cleanup(share);
229 curl_global_cleanup();
230 return (CURLcode)res;
231 }
232