1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus (at) haxx.se> 9 * Copyright (C) 2013-2015, Daniel Stenberg, <daniel (at) haxx.se>, et al. 10 * 11 * This software is licensed as described in the file COPYING, which 12 * you should have received as part of this distribution. The terms 13 * are also available at https://curl.haxx.se/docs/copyright.html. 14 * 15 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 16 * copies of the Software, and permit persons to whom the Software is 17 * furnished to do so, under the terms of the COPYING file. 18 * 19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20 * KIND, either express or implied. 21 * 22 ***************************************************************************/ 23 24 #include "curl_setup.h" 25 26 #include <curl/curl.h> 27 28 #include "urldata.h" 29 #include "url.h" 30 #include "progress.h" 31 #include "multiif.h" 32 #include "pipeline.h" 33 #include "sendf.h" 34 #include "rawstr.h" 35 36 #include "curl_memory.h" 37 /* The last #include file should be: */ 38 #include "memdebug.h" 39 40 struct site_blacklist_entry { 41 char *hostname; 42 unsigned short port; 43 }; 44 45 static void site_blacklist_llist_dtor(void *user, void *element) 46 { 47 struct site_blacklist_entry *entry = element; 48 (void)user; 49 50 Curl_safefree(entry->hostname); 51 free(entry); 52 } 53 54 static void server_blacklist_llist_dtor(void *user, void *element) 55 { 56 (void)user; 57 free(element); 58 } 59 60 bool Curl_pipeline_penalized(struct Curl_easy *data, 61 struct connectdata *conn) 62 { 63 if(data) { 64 bool penalized = FALSE; 65 curl_off_t penalty_size = 66 Curl_multi_content_length_penalty_size(data->multi); 67 curl_off_t chunk_penalty_size = 68 Curl_multi_chunk_length_penalty_size(data->multi); 69 curl_off_t recv_size = -2; /* Make it easy to spot in the log */ 70 71 /* Find the head of the recv pipe, if any */ 72 if(conn->recv_pipe && conn->recv_pipe->head) { 73 struct Curl_easy *recv_handle = conn->recv_pipe->head->ptr; 74 75 recv_size = recv_handle->req.size; 76 77 if(penalty_size > 0 && recv_size > penalty_size) 78 penalized = TRUE; 79 } 80 81 if(chunk_penalty_size > 0 && 82 (curl_off_t)conn->chunk.datasize > chunk_penalty_size) 83 penalized = TRUE; 84 85 infof(data, "Conn: %ld (%p) Receive pipe weight: (%" 86 CURL_FORMAT_CURL_OFF_T "/%zu), penalized: %s\n", 87 conn->connection_id, (void *)conn, recv_size, 88 conn->chunk.datasize, penalized?"TRUE":"FALSE"); 89 return penalized; 90 } 91 return FALSE; 92 } 93 94 static CURLcode addHandleToPipeline(struct Curl_easy *data, 95 struct curl_llist *pipeline) 96 { 97 if(!Curl_llist_insert_next(pipeline, pipeline->tail, data)) 98 return CURLE_OUT_OF_MEMORY; 99 return CURLE_OK; 100 } 101 102 103 CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle, 104 struct connectdata *conn) 105 { 106 struct curl_llist_element *sendhead = conn->send_pipe->head; 107 struct curl_llist *pipeline; 108 CURLcode result; 109 110 pipeline = conn->send_pipe; 111 112 result = addHandleToPipeline(handle, pipeline); 113 114 if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) { 115 /* this is a new one as head, expire it */ 116 Curl_pipeline_leave_write(conn); /* not in use yet */ 117 Curl_expire(conn->send_pipe->head->ptr, 1); 118 } 119 120 #if 0 /* enable for pipeline debugging */ 121 print_pipeline(conn); 122 #endif 123 124 return result; 125 } 126 127 /* Move this transfer from the sending list to the receiving list. 128 129 Pay special attention to the new sending list "leader" as it needs to get 130 checked to update what sockets it acts on. 131 132 */ 133 void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *handle, 134 struct connectdata *conn) 135 { 136 struct curl_llist_element *curr; 137 138 curr = conn->send_pipe->head; 139 while(curr) { 140 if(curr->ptr == handle) { 141 Curl_llist_move(conn->send_pipe, curr, 142 conn->recv_pipe, conn->recv_pipe->tail); 143 144 if(conn->send_pipe->head) { 145 /* Since there's a new easy handle at the start of the send pipeline, 146 set its timeout value to 1ms to make it trigger instantly */ 147 Curl_pipeline_leave_write(conn); /* not used now */ 148 #ifdef DEBUGBUILD 149 infof(conn->data, "%p is at send pipe head B!\n", 150 (void *)conn->send_pipe->head->ptr); 151 #endif 152 Curl_expire(conn->send_pipe->head->ptr, 1); 153 } 154 155 /* The receiver's list is not really interesting here since either this 156 handle is now first in the list and we'll deal with it soon, or 157 another handle is already first and thus is already taken care of */ 158 159 break; /* we're done! */ 160 } 161 curr = curr->next; 162 } 163 } 164 165 bool Curl_pipeline_site_blacklisted(struct Curl_easy *handle, 166 struct connectdata *conn) 167 { 168 if(handle->multi) { 169 struct curl_llist *blacklist = 170 Curl_multi_pipelining_site_bl(handle->multi); 171 172 if(blacklist) { 173 struct curl_llist_element *curr; 174 175 curr = blacklist->head; 176 while(curr) { 177 struct site_blacklist_entry *site; 178 179 site = curr->ptr; 180 if(Curl_raw_equal(site->hostname, conn->host.name) && 181 site->port == conn->remote_port) { 182 infof(handle, "Site %s:%d is pipeline blacklisted\n", 183 conn->host.name, conn->remote_port); 184 return TRUE; 185 } 186 curr = curr->next; 187 } 188 } 189 } 190 return FALSE; 191 } 192 193 CURLMcode Curl_pipeline_set_site_blacklist(char **sites, 194 struct curl_llist **list_ptr) 195 { 196 struct curl_llist *old_list = *list_ptr; 197 struct curl_llist *new_list = NULL; 198 199 if(sites) { 200 new_list = Curl_llist_alloc((curl_llist_dtor) site_blacklist_llist_dtor); 201 if(!new_list) 202 return CURLM_OUT_OF_MEMORY; 203 204 /* Parse the URLs and populate the list */ 205 while(*sites) { 206 char *hostname; 207 char *port; 208 struct site_blacklist_entry *entry; 209 210 hostname = strdup(*sites); 211 if(!hostname) { 212 Curl_llist_destroy(new_list, NULL); 213 return CURLM_OUT_OF_MEMORY; 214 } 215 216 entry = malloc(sizeof(struct site_blacklist_entry)); 217 if(!entry) { 218 free(hostname); 219 Curl_llist_destroy(new_list, NULL); 220 return CURLM_OUT_OF_MEMORY; 221 } 222 223 port = strchr(hostname, ':'); 224 if(port) { 225 *port = '\0'; 226 port++; 227 entry->port = (unsigned short)strtol(port, NULL, 10); 228 } 229 else { 230 /* Default port number for HTTP */ 231 entry->port = 80; 232 } 233 234 entry->hostname = hostname; 235 236 if(!Curl_llist_insert_next(new_list, new_list->tail, entry)) { 237 site_blacklist_llist_dtor(NULL, entry); 238 Curl_llist_destroy(new_list, NULL); 239 return CURLM_OUT_OF_MEMORY; 240 } 241 242 sites++; 243 } 244 } 245 246 /* Free the old list */ 247 if(old_list) { 248 Curl_llist_destroy(old_list, NULL); 249 } 250 251 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */ 252 *list_ptr = new_list; 253 254 return CURLM_OK; 255 } 256 257 bool Curl_pipeline_server_blacklisted(struct Curl_easy *handle, 258 char *server_name) 259 { 260 if(handle->multi && server_name) { 261 struct curl_llist *blacklist = 262 Curl_multi_pipelining_server_bl(handle->multi); 263 264 if(blacklist) { 265 struct curl_llist_element *curr; 266 267 curr = blacklist->head; 268 while(curr) { 269 char *bl_server_name; 270 271 bl_server_name = curr->ptr; 272 if(Curl_raw_nequal(bl_server_name, server_name, 273 strlen(bl_server_name))) { 274 infof(handle, "Server %s is blacklisted\n", server_name); 275 return TRUE; 276 } 277 curr = curr->next; 278 } 279 } 280 281 DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name)); 282 } 283 return FALSE; 284 } 285 286 CURLMcode Curl_pipeline_set_server_blacklist(char **servers, 287 struct curl_llist **list_ptr) 288 { 289 struct curl_llist *old_list = *list_ptr; 290 struct curl_llist *new_list = NULL; 291 292 if(servers) { 293 new_list = Curl_llist_alloc((curl_llist_dtor) server_blacklist_llist_dtor); 294 if(!new_list) 295 return CURLM_OUT_OF_MEMORY; 296 297 /* Parse the URLs and populate the list */ 298 while(*servers) { 299 char *server_name; 300 301 server_name = strdup(*servers); 302 if(!server_name) { 303 Curl_llist_destroy(new_list, NULL); 304 return CURLM_OUT_OF_MEMORY; 305 } 306 307 if(!Curl_llist_insert_next(new_list, new_list->tail, server_name)) { 308 Curl_llist_destroy(new_list, NULL); 309 Curl_safefree(server_name); 310 return CURLM_OUT_OF_MEMORY; 311 } 312 313 servers++; 314 } 315 } 316 317 /* Free the old list */ 318 if(old_list) { 319 Curl_llist_destroy(old_list, NULL); 320 } 321 322 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */ 323 *list_ptr = new_list; 324 325 return CURLM_OK; 326 } 327 328 static bool pipe_head(struct Curl_easy *data, 329 struct curl_llist *pipeline) 330 { 331 if(pipeline) { 332 struct curl_llist_element *curr = pipeline->head; 333 if(curr) 334 return (curr->ptr == data) ? TRUE : FALSE; 335 } 336 return FALSE; 337 } 338 339 /* returns TRUE if the given handle is head of the recv pipe */ 340 bool Curl_recvpipe_head(struct Curl_easy *data, 341 struct connectdata *conn) 342 { 343 return pipe_head(data, conn->recv_pipe); 344 } 345 346 /* returns TRUE if the given handle is head of the send pipe */ 347 bool Curl_sendpipe_head(struct Curl_easy *data, 348 struct connectdata *conn) 349 { 350 return pipe_head(data, conn->send_pipe); 351 } 352 353 354 /* 355 * Check if the write channel is available and this handle as at the head, 356 * then grab the channel and return TRUE. 357 * 358 * If not available, return FALSE. 359 */ 360 361 bool Curl_pipeline_checkget_write(struct Curl_easy *data, 362 struct connectdata *conn) 363 { 364 if(conn->bits.multiplex) 365 /* when multiplexing, we can use it at once */ 366 return TRUE; 367 368 if(!conn->writechannel_inuse && Curl_sendpipe_head(data, conn)) { 369 /* Grab the channel */ 370 conn->writechannel_inuse = TRUE; 371 return TRUE; 372 } 373 return FALSE; 374 } 375 376 377 /* 378 * Check if the read channel is available and this handle as at the head, then 379 * grab the channel and return TRUE. 380 * 381 * If not available, return FALSE. 382 */ 383 384 bool Curl_pipeline_checkget_read(struct Curl_easy *data, 385 struct connectdata *conn) 386 { 387 if(conn->bits.multiplex) 388 /* when multiplexing, we can use it at once */ 389 return TRUE; 390 391 if(!conn->readchannel_inuse && Curl_recvpipe_head(data, conn)) { 392 /* Grab the channel */ 393 conn->readchannel_inuse = TRUE; 394 return TRUE; 395 } 396 return FALSE; 397 } 398 399 /* 400 * The current user of the pipeline write channel gives it up. 401 */ 402 void Curl_pipeline_leave_write(struct connectdata *conn) 403 { 404 conn->writechannel_inuse = FALSE; 405 } 406 407 /* 408 * The current user of the pipeline read channel gives it up. 409 */ 410 void Curl_pipeline_leave_read(struct connectdata *conn) 411 { 412 conn->readchannel_inuse = FALSE; 413 } 414 415 416 #if 0 417 void print_pipeline(struct connectdata *conn) 418 { 419 struct curl_llist_element *curr; 420 struct connectbundle *cb_ptr; 421 struct Curl_easy *data = conn->data; 422 423 cb_ptr = conn->bundle; 424 425 if(cb_ptr) { 426 curr = cb_ptr->conn_list->head; 427 while(curr) { 428 conn = curr->ptr; 429 infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n", 430 conn->connection_id, 431 (void *)conn, 432 conn->send_pipe->size, 433 conn->recv_pipe->size); 434 curr = curr->next; 435 } 436 } 437 } 438 439 #endif 440