tls-session-reuse.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. /* <DESC>
  25. * TLS session reuse
  26. * </DESC>
  27. */
  28. #include <curl/curl.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. /* #include <error.h> */
  33. #include <errno.h>
  34. static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type)
  35. {
  36. /*
  37. * This is the trace look that is similar to what libcurl makes on its
  38. * own.
  39. */
  40. static const char * const s_infotype[] = {
  41. "* ", "< ", "> ", "{ ", "} ", "{ ", "} "
  42. };
  43. if(idsbuf && *idsbuf)
  44. fprintf(log, "%s%s", idsbuf, s_infotype[type]);
  45. else
  46. fputs(s_infotype[type], log);
  47. }
  48. #define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] "
  49. #define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \
  50. CURL_FORMAT_CURL_OFF_T "] "
  51. /*
  52. ** callback for CURLOPT_DEBUGFUNCTION
  53. */
  54. static int debug_cb(CURL *handle, curl_infotype type,
  55. char *data, size_t size,
  56. void *userdata)
  57. {
  58. FILE *output = stderr;
  59. static int newl = 0;
  60. static int traced_data = 0;
  61. char idsbuf[60];
  62. curl_off_t xfer_id, conn_id;
  63. (void)handle; /* not used */
  64. (void)userdata;
  65. if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) {
  66. if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) &&
  67. conn_id >= 0) {
  68. curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, xfer_id,
  69. conn_id);
  70. }
  71. else {
  72. curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id);
  73. }
  74. }
  75. else
  76. idsbuf[0] = 0;
  77. switch(type) {
  78. case CURLINFO_HEADER_OUT:
  79. if(size > 0) {
  80. size_t st = 0;
  81. size_t i;
  82. for(i = 0; i < size - 1; i++) {
  83. if(data[i] == '\n') { /* LF */
  84. if(!newl) {
  85. log_line_start(output, idsbuf, type);
  86. }
  87. (void)fwrite(data + st, i - st + 1, 1, output);
  88. st = i + 1;
  89. newl = 0;
  90. }
  91. }
  92. if(!newl)
  93. log_line_start(output, idsbuf, type);
  94. (void)fwrite(data + st, i - st + 1, 1, output);
  95. }
  96. newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
  97. traced_data = 0;
  98. break;
  99. case CURLINFO_TEXT:
  100. case CURLINFO_HEADER_IN:
  101. if(!newl)
  102. log_line_start(output, idsbuf, type);
  103. (void)fwrite(data, size, 1, output);
  104. newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
  105. traced_data = 0;
  106. break;
  107. case CURLINFO_DATA_OUT:
  108. case CURLINFO_DATA_IN:
  109. case CURLINFO_SSL_DATA_IN:
  110. case CURLINFO_SSL_DATA_OUT:
  111. if(!traced_data) {
  112. if(!newl)
  113. log_line_start(output, idsbuf, type);
  114. fprintf(output, "[%ld bytes data]\n", (long)size);
  115. newl = 0;
  116. traced_data = 1;
  117. }
  118. break;
  119. default: /* nada */
  120. newl = 0;
  121. traced_data = 1;
  122. break;
  123. }
  124. return 0;
  125. }
  126. static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *opaque)
  127. {
  128. (void)ptr;
  129. (void)opaque;
  130. return size * nmemb;
  131. }
  132. static void add_transfer(CURLM *multi, CURLSH *share,
  133. struct curl_slist *resolve,
  134. const char *url, int http_version)
  135. {
  136. CURL *easy;
  137. CURLMcode mc;
  138. easy = curl_easy_init();
  139. if(!easy) {
  140. fprintf(stderr, "curl_easy_init failed\n");
  141. exit(1);
  142. }
  143. curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
  144. curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb);
  145. curl_easy_setopt(easy, CURLOPT_URL, url);
  146. curl_easy_setopt(easy, CURLOPT_SHARE, share);
  147. curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L);
  148. curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L);
  149. curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
  150. curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, http_version);
  151. curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb);
  152. curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
  153. curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
  154. curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
  155. if(resolve)
  156. curl_easy_setopt(easy, CURLOPT_RESOLVE, resolve);
  157. mc = curl_multi_add_handle(multi, easy);
  158. if(mc != CURLM_OK) {
  159. fprintf(stderr, "curl_multi_add_handle: %s\n",
  160. curl_multi_strerror(mc));
  161. exit(1);
  162. }
  163. }
  164. int main(int argc, char *argv[])
  165. {
  166. const char *url;
  167. CURLM *multi;
  168. CURLMcode mc;
  169. int running_handles = 0, numfds;
  170. CURLMsg *msg;
  171. CURLSH *share;
  172. CURLU *cu;
  173. struct curl_slist resolve;
  174. char resolve_buf[1024];
  175. int msgs_in_queue;
  176. int add_more, waits, ongoing = 0;
  177. char *host, *port;
  178. int http_version = CURL_HTTP_VERSION_1_1;
  179. if(argc != 3) {
  180. fprintf(stderr, "%s proto URL\n", argv[0]);
  181. exit(2);
  182. }
  183. if(!strcmp("h2", argv[1]))
  184. http_version = CURL_HTTP_VERSION_2;
  185. else if(!strcmp("h3", argv[1]))
  186. http_version = CURL_HTTP_VERSION_3ONLY;
  187. url = argv[2];
  188. cu = curl_url();
  189. if(!cu) {
  190. fprintf(stderr, "out of memory\n");
  191. exit(1);
  192. }
  193. if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
  194. fprintf(stderr, "not a URL: '%s'\n", url);
  195. exit(1);
  196. }
  197. if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
  198. fprintf(stderr, "could not get host of '%s'\n", url);
  199. exit(1);
  200. }
  201. if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
  202. fprintf(stderr, "could not get port of '%s'\n", url);
  203. exit(1);
  204. }
  205. memset(&resolve, 0, sizeof(resolve));
  206. curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, "%s:%s:127.0.0.1",
  207. host, port);
  208. curl_slist_append(&resolve, resolve_buf);
  209. multi = curl_multi_init();
  210. if(!multi) {
  211. fprintf(stderr, "curl_multi_init failed\n");
  212. exit(1);
  213. }
  214. share = curl_share_init();
  215. if(!share) {
  216. fprintf(stderr, "curl_share_init failed\n");
  217. exit(1);
  218. }
  219. curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
  220. add_transfer(multi, share, &resolve, url, http_version);
  221. ++ongoing;
  222. add_more = 6;
  223. waits = 3;
  224. do {
  225. mc = curl_multi_perform(multi, &running_handles);
  226. if(mc != CURLM_OK) {
  227. fprintf(stderr, "curl_multi_perform: %s\n",
  228. curl_multi_strerror(mc));
  229. exit(1);
  230. }
  231. if(running_handles) {
  232. mc = curl_multi_poll(multi, NULL, 0, 1000000, &numfds);
  233. if(mc != CURLM_OK) {
  234. fprintf(stderr, "curl_multi_poll: %s\n",
  235. curl_multi_strerror(mc));
  236. exit(1);
  237. }
  238. }
  239. if(waits) {
  240. --waits;
  241. }
  242. else {
  243. while(add_more) {
  244. add_transfer(multi, share, &resolve, url, http_version);
  245. ++ongoing;
  246. --add_more;
  247. }
  248. }
  249. /* Check for finished handles and remove. */
  250. /* !checksrc! disable EQUALSNULL 1 */
  251. while((msg = curl_multi_info_read(multi, &msgs_in_queue)) != NULL) {
  252. if(msg->msg == CURLMSG_DONE) {
  253. long status = 0;
  254. curl_off_t xfer_id;
  255. curl_easy_getinfo(msg->easy_handle, CURLINFO_XFER_ID, &xfer_id);
  256. curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
  257. if(msg->data.result == CURLE_SEND_ERROR ||
  258. msg->data.result == CURLE_RECV_ERROR) {
  259. /* We get these if the server had a GOAWAY in transit on
  260. * re-using a connection */
  261. }
  262. else if(msg->data.result) {
  263. fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
  264. ": failed with %d\n", xfer_id, msg->data.result);
  265. exit(1);
  266. }
  267. else if(status != 200) {
  268. fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
  269. ": wrong http status %ld (expected 200)\n", xfer_id, status);
  270. exit(1);
  271. }
  272. curl_multi_remove_handle(multi, msg->easy_handle);
  273. curl_easy_cleanup(msg->easy_handle);
  274. --ongoing;
  275. fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T" retiring "
  276. "(%d now running)\n", xfer_id, running_handles);
  277. }
  278. }
  279. fprintf(stderr, "running_handles=%d, yet_to_start=%d\n",
  280. running_handles, add_more);
  281. } while(ongoing || add_more);
  282. fprintf(stderr, "exiting\n");
  283. exit(EXIT_SUCCESS);
  284. }