1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #include <curl/curl.h>
36 #include "curl_memory.h"
37 /* The last #include file should be: */
40 struct site_blacklist_entry {
45 static void site_blacklist_llist_dtor(void *user, void *element)
47 struct site_blacklist_entry *entry = element;
50 Curl_safefree(entry->hostname);
54 static void server_blacklist_llist_dtor(void *user, void *element)
56 char *server_name = element;
59 Curl_safefree(server_name);
62 bool Curl_pipeline_penalized(struct SessionHandle *data,
63 struct connectdata *conn)
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 */
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;
77 recv_size = recv_handle->req.size;
79 if(penalty_size > 0 && recv_size > penalty_size)
83 if(chunk_penalty_size > 0 &&
84 (curl_off_t)conn->chunk.datasize > chunk_penalty_size)
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);
95 /* Find the best connection in a bundle to use for the next request */
97 Curl_bundle_find_best(struct SessionHandle *data,
98 struct connectbundle *cb_ptr)
100 struct curl_llist_element *curr;
101 struct connectdata *conn;
102 struct connectdata *best_conn = NULL;
104 size_t best_pipe_len = 99;
108 curr = cb_ptr->conn_list->head;
111 pipe_len = conn->send_pipe->size + conn->recv_pipe->size;
113 if(!Curl_pipeline_penalized(conn->data, conn) &&
114 pipe_len < best_pipe_len) {
116 best_pipe_len = pipe_len;
121 /* If we haven't found a connection, i.e all pipelines are penalized
122 or full, just pick one. The request will then be queued in
123 Curl_add_handle_to_pipeline(). */
125 best_conn = cb_ptr->conn_list->head->ptr;
130 CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle,
131 struct connectdata *conn)
133 struct curl_llist_element *sendhead = conn->send_pipe->head;
134 struct curl_llist *pipeline;
137 pipeline = conn->send_pipe;
139 infof(conn->data, "Adding handle: conn: %p\n", conn);
140 infof(conn->data, "Adding handle: send: %d\n", conn->send_pipe->size);
141 infof(conn->data, "Adding handle: recv: %d\n", conn->recv_pipe->size);
142 rc = Curl_addHandleToPipeline(handle, pipeline);
144 if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) {
145 /* this is a new one as head, expire it */
146 conn->writechannel_inuse = FALSE; /* not in use yet */
148 infof(conn->data, "%p is at send pipe head!\n",
149 conn->send_pipe->head->ptr);
151 Curl_expire(conn->send_pipe->head->ptr, 1);
154 print_pipeline(conn);
159 /* Move this transfer from the sending list to the receiving list.
161 Pay special attention to the new sending list "leader" as it needs to get
162 checked to update what sockets it acts on.
165 void Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle *handle,
166 struct connectdata *conn)
168 struct curl_llist_element *curr;
170 curr = conn->send_pipe->head;
172 if(curr->ptr == handle) {
173 Curl_llist_move(conn->send_pipe, curr,
174 conn->recv_pipe, conn->recv_pipe->tail);
176 if(conn->send_pipe->head) {
177 /* Since there's a new easy handle at the start of the send pipeline,
178 set its timeout value to 1ms to make it trigger instantly */
179 conn->writechannel_inuse = FALSE; /* not used now */
181 infof(conn->data, "%p is at send pipe head B!\n",
182 conn->send_pipe->head->ptr);
184 Curl_expire(conn->send_pipe->head->ptr, 1);
187 /* The receiver's list is not really interesting here since either this
188 handle is now first in the list and we'll deal with it soon, or
189 another handle is already first and thus is already taken care of */
191 break; /* we're done! */
197 bool Curl_pipeline_site_blacklisted(struct SessionHandle *handle,
198 struct connectdata *conn)
201 struct curl_llist *blacklist =
202 Curl_multi_pipelining_site_bl(handle->multi);
205 struct curl_llist_element *curr;
207 curr = blacklist->head;
209 struct site_blacklist_entry *site;
212 if(Curl_raw_equal(site->hostname, conn->host.name) &&
213 site->port == conn->remote_port) {
214 infof(handle, "Site %s:%d is pipeline blacklisted\n",
215 conn->host.name, conn->remote_port);
225 CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
226 struct curl_llist **list_ptr)
228 struct curl_llist *old_list = *list_ptr;
229 struct curl_llist *new_list = NULL;
232 new_list = Curl_llist_alloc((curl_llist_dtor) site_blacklist_llist_dtor);
234 return CURLM_OUT_OF_MEMORY;
236 /* Parse the URLs and populate the list */
240 struct site_blacklist_entry *entry;
242 entry = malloc(sizeof(struct site_blacklist_entry));
244 hostname = strdup(*sites);
246 return CURLM_OUT_OF_MEMORY;
248 port = strchr(hostname, ':');
252 entry->port = (unsigned short)strtol(port, NULL, 10);
255 /* Default port number for HTTP */
259 entry->hostname = hostname;
261 if(!Curl_llist_insert_next(new_list, new_list->tail, entry))
262 return CURLM_OUT_OF_MEMORY;
268 /* Free the old list */
270 Curl_llist_destroy(old_list, NULL);
273 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
274 *list_ptr = new_list;
279 bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle,
283 struct curl_llist *blacklist =
284 Curl_multi_pipelining_server_bl(handle->multi);
287 struct curl_llist_element *curr;
289 curr = blacklist->head;
291 char *bl_server_name;
293 bl_server_name = curr->ptr;
294 if(Curl_raw_nequal(bl_server_name, server_name,
295 strlen(bl_server_name))) {
296 infof(handle, "Server %s is blacklisted\n", server_name);
303 infof(handle, "Server %s is not blacklisted\n", server_name);
308 CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
309 struct curl_llist **list_ptr)
311 struct curl_llist *old_list = *list_ptr;
312 struct curl_llist *new_list = NULL;
315 new_list = Curl_llist_alloc((curl_llist_dtor) server_blacklist_llist_dtor);
317 return CURLM_OUT_OF_MEMORY;
319 /* Parse the URLs and populate the list */
323 server_name = strdup(*servers);
325 return CURLM_OUT_OF_MEMORY;
327 if(!Curl_llist_insert_next(new_list, new_list->tail, server_name))
328 return CURLM_OUT_OF_MEMORY;
334 /* Free the old list */
336 Curl_llist_destroy(old_list, NULL);
339 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
340 *list_ptr = new_list;
346 void print_pipeline(struct connectdata *conn)
348 struct curl_llist_element *curr;
349 struct connectbundle *cb_ptr;
350 struct SessionHandle *data = conn->data;
352 cb_ptr = conn->bundle;
355 curr = cb_ptr->conn_list->head;
358 infof(data, "- Conn %d (%p) send_pipe: %d, recv_pipe: %d\n",
361 conn->send_pipe->size,
362 conn->recv_pipe->size);