1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus (at) haxx.se> 9 * Copyright (C) 2013 - 2018, 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 "strcase.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 struct curl_llist_element list; 42 unsigned short port; 43 char hostname[1]; 44 }; 45 46 static void site_blacklist_llist_dtor(void *user, void *element) 47 { 48 struct site_blacklist_entry *entry = element; 49 (void)user; 50 free(entry); 51 } 52 53 static void server_blacklist_llist_dtor(void *user, void *element) 54 { 55 (void)user; 56 free(element); 57 } 58 59 bool Curl_pipeline_penalized(struct Curl_easy *data, 60 struct connectdata *conn) 61 { 62 if(data) { 63 bool penalized = FALSE; 64 curl_off_t penalty_size = 65 Curl_multi_content_length_penalty_size(data->multi); 66 curl_off_t chunk_penalty_size = 67 Curl_multi_chunk_length_penalty_size(data->multi); 68 curl_off_t recv_size = -2; /* Make it easy to spot in the log */ 69 70 /* Find the head of the recv pipe, if any */ 71 if(conn->recv_pipe.head) { 72 struct Curl_easy *recv_handle = conn->recv_pipe.head->ptr; 73 74 recv_size = recv_handle->req.size; 75 76 if(penalty_size > 0 && recv_size > penalty_size) 77 penalized = TRUE; 78 } 79 80 if(chunk_penalty_size > 0 && 81 (curl_off_t)conn->chunk.datasize > chunk_penalty_size) 82 penalized = TRUE; 83 84 infof(data, "Conn: %ld (%p) Receive pipe weight: (%" 85 CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T 86 "), 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 Curl_llist_insert_next(pipeline, pipeline->tail, data, 98 &data->pipeline_queue); 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 if((conn->bundle->multiuse == BUNDLE_PIPELINING) && 114 (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, 0, EXPIRE_RUN_NOW); 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, 0, EXPIRE_RUN_NOW); 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(strcasecompare(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) 195 { 196 /* Free the old list */ 197 if(list->size) 198 Curl_llist_destroy(list, NULL); 199 200 if(sites) { 201 Curl_llist_init(list, (curl_llist_dtor) site_blacklist_llist_dtor); 202 203 /* Parse the URLs and populate the list */ 204 while(*sites) { 205 char *port; 206 struct site_blacklist_entry *entry; 207 208 entry = malloc(sizeof(struct site_blacklist_entry) + strlen(*sites)); 209 if(!entry) { 210 Curl_llist_destroy(list, NULL); 211 return CURLM_OUT_OF_MEMORY; 212 } 213 strcpy(entry->hostname, *sites); 214 215 port = strchr(entry->hostname, ':'); 216 if(port) { 217 *port = '\0'; 218 port++; 219 entry->port = (unsigned short)strtol(port, NULL, 10); 220 } 221 else { 222 /* Default port number for HTTP */ 223 entry->port = 80; 224 } 225 226 Curl_llist_insert_next(list, list->tail, entry, &entry->list); 227 sites++; 228 } 229 } 230 231 return CURLM_OK; 232 } 233 234 struct blacklist_node { 235 struct curl_llist_element list; 236 char server_name[1]; 237 }; 238 239 bool Curl_pipeline_server_blacklisted(struct Curl_easy *handle, 240 char *server_name) 241 { 242 if(handle->multi && server_name) { 243 struct curl_llist *list = 244 Curl_multi_pipelining_server_bl(handle->multi); 245 246 struct curl_llist_element *e = list->head; 247 while(e) { 248 struct blacklist_node *bl = (struct blacklist_node *)e; 249 if(strncasecompare(bl->server_name, server_name, 250 strlen(bl->server_name))) { 251 infof(handle, "Server %s is blacklisted\n", server_name); 252 return TRUE; 253 } 254 e = e->next; 255 } 256 257 DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name)); 258 } 259 return FALSE; 260 } 261 262 CURLMcode Curl_pipeline_set_server_blacklist(char **servers, 263 struct curl_llist *list) 264 { 265 /* Free the old list */ 266 if(list->size) 267 Curl_llist_destroy(list, NULL); 268 269 if(servers) { 270 Curl_llist_init(list, (curl_llist_dtor) server_blacklist_llist_dtor); 271 272 /* Parse the URLs and populate the list */ 273 while(*servers) { 274 struct blacklist_node *n; 275 size_t len = strlen(*servers); 276 277 n = malloc(sizeof(struct blacklist_node) + len); 278 if(!n) { 279 Curl_llist_destroy(list, NULL); 280 return CURLM_OUT_OF_MEMORY; 281 } 282 strcpy(n->server_name, *servers); 283 284 Curl_llist_insert_next(list, list->tail, n, &n->list); 285 servers++; 286 } 287 } 288 289 290 return CURLM_OK; 291 } 292 293 static bool pipe_head(struct Curl_easy *data, 294 struct curl_llist *pipeline) 295 { 296 if(pipeline) { 297 struct curl_llist_element *curr = pipeline->head; 298 if(curr) 299 return (curr->ptr == data) ? TRUE : FALSE; 300 } 301 return FALSE; 302 } 303 304 /* returns TRUE if the given handle is head of the recv pipe */ 305 bool Curl_recvpipe_head(struct Curl_easy *data, 306 struct connectdata *conn) 307 { 308 return pipe_head(data, &conn->recv_pipe); 309 } 310 311 /* returns TRUE if the given handle is head of the send pipe */ 312 bool Curl_sendpipe_head(struct Curl_easy *data, 313 struct connectdata *conn) 314 { 315 return pipe_head(data, &conn->send_pipe); 316 } 317 318 319 /* 320 * Check if the write channel is available and this handle as at the head, 321 * then grab the channel and return TRUE. 322 * 323 * If not available, return FALSE. 324 */ 325 326 bool Curl_pipeline_checkget_write(struct Curl_easy *data, 327 struct connectdata *conn) 328 { 329 if(conn->bits.multiplex) 330 /* when multiplexing, we can use it at once */ 331 return TRUE; 332 333 if(!conn->writechannel_inuse && Curl_sendpipe_head(data, conn)) { 334 /* Grab the channel */ 335 conn->writechannel_inuse = TRUE; 336 return TRUE; 337 } 338 return FALSE; 339 } 340 341 342 /* 343 * Check if the read channel is available and this handle as at the head, then 344 * grab the channel and return TRUE. 345 * 346 * If not available, return FALSE. 347 */ 348 349 bool Curl_pipeline_checkget_read(struct Curl_easy *data, 350 struct connectdata *conn) 351 { 352 if(conn->bits.multiplex) 353 /* when multiplexing, we can use it at once */ 354 return TRUE; 355 356 if(!conn->readchannel_inuse && Curl_recvpipe_head(data, conn)) { 357 /* Grab the channel */ 358 conn->readchannel_inuse = TRUE; 359 return TRUE; 360 } 361 return FALSE; 362 } 363 364 /* 365 * The current user of the pipeline write channel gives it up. 366 */ 367 void Curl_pipeline_leave_write(struct connectdata *conn) 368 { 369 conn->writechannel_inuse = FALSE; 370 } 371 372 /* 373 * The current user of the pipeline read channel gives it up. 374 */ 375 void Curl_pipeline_leave_read(struct connectdata *conn) 376 { 377 conn->readchannel_inuse = FALSE; 378 } 379 380 381 #if 0 382 void print_pipeline(struct connectdata *conn) 383 { 384 struct curl_llist_element *curr; 385 struct connectbundle *cb_ptr; 386 struct Curl_easy *data = conn->data; 387 388 cb_ptr = conn->bundle; 389 390 if(cb_ptr) { 391 curr = cb_ptr->conn_list->head; 392 while(curr) { 393 conn = curr->ptr; 394 infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n", 395 conn->connection_id, 396 (void *)conn, 397 conn->send_pipe->size, 398 conn->recv_pipe->size); 399 curr = curr->next; 400 } 401 } 402 } 403 404 #endif 405