1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) 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.haxx.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 ***************************************************************************/ 22 23 /* 24 * This test sends data with CURLOPT_KEEP_SENDING_ON_ERROR. 25 * The server responds with an early error response. 26 * The test is successful if the connection can be reused for the next request, 27 * because this implies that the data has been sent completely to the server. 28 */ 29 30 #include "test.h" 31 32 #include "memdebug.h" 33 34 struct cb_data { 35 CURL *easy_handle; 36 int response_received; 37 int paused; 38 size_t remaining_bytes; 39 }; 40 41 42 static void reset_data(struct cb_data *data, CURL *curl) 43 { 44 data->easy_handle = curl; 45 data->response_received = 0; 46 data->paused = 0; 47 data->remaining_bytes = 3; 48 } 49 50 51 static size_t read_callback(void *ptr, size_t size, size_t nitems, 52 void *userdata) 53 { 54 struct cb_data *data = (struct cb_data *)userdata; 55 56 /* wait until the server has sent all response headers */ 57 if(data->response_received) { 58 size_t totalsize = nitems * size; 59 60 size_t bytes_to_send = data->remaining_bytes; 61 if(bytes_to_send > totalsize) { 62 bytes_to_send = totalsize; 63 } 64 65 memset(ptr, 'a', bytes_to_send); 66 data->remaining_bytes -= bytes_to_send; 67 68 return bytes_to_send; 69 } 70 else { 71 data->paused = 1; 72 return CURL_READFUNC_PAUSE; 73 } 74 } 75 76 77 static size_t write_callback(char *ptr, size_t size, size_t nmemb, 78 void *userdata) 79 { 80 struct cb_data *data = (struct cb_data *)userdata; 81 size_t totalsize = nmemb * size; 82 83 /* unused parameter */ 84 (void)ptr; 85 86 /* all response headers have been received */ 87 data->response_received = 1; 88 89 if(data->paused) { 90 /* continue to send request body data */ 91 data->paused = 0; 92 curl_easy_pause(data->easy_handle, CURLPAUSE_CONT); 93 } 94 95 return totalsize; 96 } 97 98 99 static int perform_and_check_connections(CURL *curl, const char *description, 100 long expected_connections) 101 { 102 CURLcode res; 103 long connections = 0; 104 105 res = curl_easy_perform(curl); 106 if(res != CURLE_OK) { 107 fprintf(stderr, "curl_easy_perform() failed\n"); 108 return TEST_ERR_MAJOR_BAD; 109 } 110 111 res = curl_easy_getinfo(curl, CURLINFO_NUM_CONNECTS, &connections); 112 if(res != CURLE_OK) { 113 fprintf(stderr, "curl_easy_getinfo() failed\n"); 114 return TEST_ERR_MAJOR_BAD; 115 } 116 117 fprintf(stderr, "%s: expected: %ld connections; actual: %ld connections\n", 118 description, expected_connections, connections); 119 120 if(connections != expected_connections) { 121 return TEST_ERR_FAILURE; 122 } 123 124 return TEST_ERR_SUCCESS; 125 } 126 127 128 int test(char *URL) 129 { 130 struct cb_data data; 131 CURL *curl = NULL; 132 CURLcode res = CURLE_FAILED_INIT; 133 134 if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { 135 fprintf(stderr, "curl_global_init() failed\n"); 136 return TEST_ERR_MAJOR_BAD; 137 } 138 139 curl = curl_easy_init(); 140 if(curl == NULL) { 141 fprintf(stderr, "curl_easy_init() failed\n"); 142 curl_global_cleanup(); 143 return TEST_ERR_MAJOR_BAD; 144 } 145 146 reset_data(&data, curl); 147 148 test_setopt(curl, CURLOPT_URL, URL); 149 test_setopt(curl, CURLOPT_POST, 1L); 150 test_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, 151 (curl_off_t)data.remaining_bytes); 152 test_setopt(curl, CURLOPT_VERBOSE, 1L); 153 test_setopt(curl, CURLOPT_READFUNCTION, read_callback); 154 test_setopt(curl, CURLOPT_READDATA, &data); 155 test_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); 156 test_setopt(curl, CURLOPT_WRITEDATA, &data); 157 158 res = perform_and_check_connections(curl, 159 "First request without CURLOPT_KEEP_SENDING_ON_ERROR", 1); 160 if(res != TEST_ERR_SUCCESS) { 161 goto test_cleanup; 162 } 163 164 reset_data(&data, curl); 165 166 res = perform_and_check_connections(curl, 167 "Second request without CURLOPT_KEEP_SENDING_ON_ERROR", 1); 168 if(res != TEST_ERR_SUCCESS) { 169 goto test_cleanup; 170 } 171 172 test_setopt(curl, CURLOPT_KEEP_SENDING_ON_ERROR, 1L); 173 174 reset_data(&data, curl); 175 176 res = perform_and_check_connections(curl, 177 "First request with CURLOPT_KEEP_SENDING_ON_ERROR", 1); 178 if(res != TEST_ERR_SUCCESS) { 179 goto test_cleanup; 180 } 181 182 reset_data(&data, curl); 183 184 res = perform_and_check_connections(curl, 185 "Second request with CURLOPT_KEEP_SENDING_ON_ERROR", 0); 186 if(res != TEST_ERR_SUCCESS) { 187 goto test_cleanup; 188 } 189 190 res = TEST_ERR_SUCCESS; 191 192 test_cleanup: 193 194 curl_easy_cleanup(curl); 195 196 curl_global_cleanup(); 197 198 return (int)res; 199 } 200