1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se> 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 http://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#include "curl_setup.h" 24 25#include <curl/curl.h> 26 27#include "urldata.h" 28#include "url.h" 29#include "progress.h" 30#include "multiif.h" 31#include "pipeline.h" 32#include "sendf.h" 33#include "rawstr.h" 34#include "bundles.h" 35 36#include "curl_memory.h" 37/* The last #include file should be: */ 38#include "memdebug.h" 39 40struct site_blacklist_entry { 41 char *hostname; 42 unsigned short port; 43}; 44 45static 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 Curl_safefree(entry); 52} 53 54static void server_blacklist_llist_dtor(void *user, void *element) 55{ 56 char *server_name = element; 57 (void)user; 58 59 Curl_safefree(server_name); 60} 61 62bool Curl_pipeline_penalized(struct SessionHandle *data, 63 struct connectdata *conn) 64{ 65 if(data) { 66 bool penalized = FALSE; 67 curl_off_t penalty_size = 68 Curl_multi_content_length_penalty_size(data->multi); 69 curl_off_t chunk_penalty_size = 70 Curl_multi_chunk_length_penalty_size(data->multi); 71 curl_off_t recv_size = -2; /* Make it easy to spot in the log */ 72 73 /* Find the head of the recv pipe, if any */ 74 if(conn->recv_pipe && conn->recv_pipe->head) { 75 struct SessionHandle *recv_handle = conn->recv_pipe->head->ptr; 76 77 recv_size = recv_handle->req.size; 78 79 if(penalty_size > 0 && recv_size > penalty_size) 80 penalized = TRUE; 81 } 82 83 if(chunk_penalty_size > 0 && 84 (curl_off_t)conn->chunk.datasize > chunk_penalty_size) 85 penalized = TRUE; 86 87 infof(data, "Conn: %d (%p) Receive pipe weight: (%d/%d), penalized: %d\n", 88 conn->connection_id, conn, recv_size, 89 conn->chunk.datasize, penalized); 90 return penalized; 91 } 92 return FALSE; 93} 94 95CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle, 96 struct connectdata *conn) 97{ 98 struct curl_llist_element *sendhead = conn->send_pipe->head; 99 struct curl_llist *pipeline; 100 CURLcode rc; 101 102 pipeline = conn->send_pipe; 103 104 infof(conn->data, "Adding handle: conn: %p\n", conn); 105 infof(conn->data, "Adding handle: send: %d\n", conn->send_pipe->size); 106 infof(conn->data, "Adding handle: recv: %d\n", conn->recv_pipe->size); 107 rc = Curl_addHandleToPipeline(handle, pipeline); 108 109 if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) { 110 /* this is a new one as head, expire it */ 111 conn->writechannel_inuse = FALSE; /* not in use yet */ 112#ifdef DEBUGBUILD 113 infof(conn->data, "%p is at send pipe head!\n", 114 conn->send_pipe->head->ptr); 115#endif 116 Curl_expire(conn->send_pipe->head->ptr, 1); 117 } 118 119 print_pipeline(conn); 120 121 return rc; 122} 123 124/* Move this transfer from the sending list to the receiving list. 125 126 Pay special attention to the new sending list "leader" as it needs to get 127 checked to update what sockets it acts on. 128 129*/ 130void Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle *handle, 131 struct connectdata *conn) 132{ 133 struct curl_llist_element *curr; 134 135 curr = conn->send_pipe->head; 136 while(curr) { 137 if(curr->ptr == handle) { 138 Curl_llist_move(conn->send_pipe, curr, 139 conn->recv_pipe, conn->recv_pipe->tail); 140 141 if(conn->send_pipe->head) { 142 /* Since there's a new easy handle at the start of the send pipeline, 143 set its timeout value to 1ms to make it trigger instantly */ 144 conn->writechannel_inuse = FALSE; /* not used now */ 145#ifdef DEBUGBUILD 146 infof(conn->data, "%p is at send pipe head B!\n", 147 conn->send_pipe->head->ptr); 148#endif 149 Curl_expire(conn->send_pipe->head->ptr, 1); 150 } 151 152 /* The receiver's list is not really interesting here since either this 153 handle is now first in the list and we'll deal with it soon, or 154 another handle is already first and thus is already taken care of */ 155 156 break; /* we're done! */ 157 } 158 curr = curr->next; 159 } 160} 161 162bool Curl_pipeline_site_blacklisted(struct SessionHandle *handle, 163 struct connectdata *conn) 164{ 165 if(handle->multi) { 166 struct curl_llist *blacklist = 167 Curl_multi_pipelining_site_bl(handle->multi); 168 169 if(blacklist) { 170 struct curl_llist_element *curr; 171 172 curr = blacklist->head; 173 while(curr) { 174 struct site_blacklist_entry *site; 175 176 site = curr->ptr; 177 if(Curl_raw_equal(site->hostname, conn->host.name) && 178 site->port == conn->remote_port) { 179 infof(handle, "Site %s:%d is pipeline blacklisted\n", 180 conn->host.name, conn->remote_port); 181 return TRUE; 182 } 183 curr = curr->next; 184 } 185 } 186 } 187 return FALSE; 188} 189 190CURLMcode Curl_pipeline_set_site_blacklist(char **sites, 191 struct curl_llist **list_ptr) 192{ 193 struct curl_llist *old_list = *list_ptr; 194 struct curl_llist *new_list = NULL; 195 196 if(sites) { 197 new_list = Curl_llist_alloc((curl_llist_dtor) site_blacklist_llist_dtor); 198 if(!new_list) 199 return CURLM_OUT_OF_MEMORY; 200 201 /* Parse the URLs and populate the list */ 202 while(*sites) { 203 char *hostname; 204 char *port; 205 struct site_blacklist_entry *entry; 206 207 entry = malloc(sizeof(struct site_blacklist_entry)); 208 209 hostname = strdup(*sites); 210 if(!hostname) 211 return CURLM_OUT_OF_MEMORY; 212 213 port = strchr(hostname, ':'); 214 if(port) { 215 *port = '\0'; 216 port++; 217 entry->port = (unsigned short)strtol(port, NULL, 10); 218 } 219 else { 220 /* Default port number for HTTP */ 221 entry->port = 80; 222 } 223 224 entry->hostname = hostname; 225 226 if(!Curl_llist_insert_next(new_list, new_list->tail, entry)) 227 return CURLM_OUT_OF_MEMORY; 228 229 sites++; 230 } 231 } 232 233 /* Free the old list */ 234 if(old_list) { 235 Curl_llist_destroy(old_list, NULL); 236 } 237 238 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */ 239 *list_ptr = new_list; 240 241 return CURLM_OK; 242} 243 244bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle, 245 char *server_name) 246{ 247 if(handle->multi) { 248 struct curl_llist *blacklist = 249 Curl_multi_pipelining_server_bl(handle->multi); 250 251 if(blacklist) { 252 struct curl_llist_element *curr; 253 254 curr = blacklist->head; 255 while(curr) { 256 char *bl_server_name; 257 258 bl_server_name = curr->ptr; 259 if(Curl_raw_nequal(bl_server_name, server_name, 260 strlen(bl_server_name))) { 261 infof(handle, "Server %s is blacklisted\n", server_name); 262 return TRUE; 263 } 264 curr = curr->next; 265 } 266 } 267 268 infof(handle, "Server %s is not blacklisted\n", server_name); 269 } 270 return FALSE; 271} 272 273CURLMcode Curl_pipeline_set_server_blacklist(char **servers, 274 struct curl_llist **list_ptr) 275{ 276 struct curl_llist *old_list = *list_ptr; 277 struct curl_llist *new_list = NULL; 278 279 if(servers) { 280 new_list = Curl_llist_alloc((curl_llist_dtor) server_blacklist_llist_dtor); 281 if(!new_list) 282 return CURLM_OUT_OF_MEMORY; 283 284 /* Parse the URLs and populate the list */ 285 while(*servers) { 286 char *server_name; 287 288 server_name = strdup(*servers); 289 if(!server_name) 290 return CURLM_OUT_OF_MEMORY; 291 292 if(!Curl_llist_insert_next(new_list, new_list->tail, server_name)) 293 return CURLM_OUT_OF_MEMORY; 294 295 servers++; 296 } 297 } 298 299 /* Free the old list */ 300 if(old_list) { 301 Curl_llist_destroy(old_list, NULL); 302 } 303 304 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */ 305 *list_ptr = new_list; 306 307 return CURLM_OK; 308} 309 310 311void print_pipeline(struct connectdata *conn) 312{ 313 struct curl_llist_element *curr; 314 struct connectbundle *cb_ptr; 315 struct SessionHandle *data = conn->data; 316 317 cb_ptr = conn->bundle; 318 319 if(cb_ptr) { 320 curr = cb_ptr->conn_list->head; 321 while(curr) { 322 conn = curr->ptr; 323 infof(data, "- Conn %d (%p) send_pipe: %d, recv_pipe: %d\n", 324 conn->connection_id, 325 conn, 326 conn->send_pipe->size, 327 conn->recv_pipe->size); 328 curr = curr->next; 329 } 330 } 331} 332