1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2013-2015, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ***************************************************************************/
24 #include "curl_setup.h"
26 #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)
60 bool Curl_pipeline_penalized(struct SessionHandle *data,
61 struct connectdata *conn)
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 */
71 /* Find the head of the recv pipe, if any */
72 if(conn->recv_pipe && conn->recv_pipe->head) {
73 struct SessionHandle *recv_handle = conn->recv_pipe->head->ptr;
75 recv_size = recv_handle->req.size;
77 if(penalty_size > 0 && recv_size > penalty_size)
81 if(chunk_penalty_size > 0 &&
82 (curl_off_t)conn->chunk.datasize > chunk_penalty_size)
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");
94 static CURLcode addHandleToPipeline(struct SessionHandle *data,
95 struct curl_llist *pipeline)
97 if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
98 return CURLE_OUT_OF_MEMORY;
103 CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle,
104 struct connectdata *conn)
106 struct curl_llist_element *sendhead = conn->send_pipe->head;
107 struct curl_llist *pipeline;
110 pipeline = conn->send_pipe;
112 result = addHandleToPipeline(handle, pipeline);
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);
120 #if 0 /* enable for pipeline debugging */
121 print_pipeline(conn);
127 /* Move this transfer from the sending list to the receiving list.
129 Pay special attention to the new sending list "leader" as it needs to get
130 checked to update what sockets it acts on.
133 void Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle *handle,
134 struct connectdata *conn)
136 struct curl_llist_element *curr;
138 curr = conn->send_pipe->head;
140 if(curr->ptr == handle) {
141 Curl_llist_move(conn->send_pipe, curr,
142 conn->recv_pipe, conn->recv_pipe->tail);
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 */
149 infof(conn->data, "%p is at send pipe head B!\n",
150 (void *)conn->send_pipe->head->ptr);
152 Curl_expire(conn->send_pipe->head->ptr, 1);
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 */
159 break; /* we're done! */
165 bool Curl_pipeline_site_blacklisted(struct SessionHandle *handle,
166 struct connectdata *conn)
169 struct curl_llist *blacklist =
170 Curl_multi_pipelining_site_bl(handle->multi);
173 struct curl_llist_element *curr;
175 curr = blacklist->head;
177 struct site_blacklist_entry *site;
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);
193 CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
194 struct curl_llist **list_ptr)
196 struct curl_llist *old_list = *list_ptr;
197 struct curl_llist *new_list = NULL;
200 new_list = Curl_llist_alloc((curl_llist_dtor) site_blacklist_llist_dtor);
202 return CURLM_OUT_OF_MEMORY;
204 /* Parse the URLs and populate the list */
208 struct site_blacklist_entry *entry;
210 hostname = strdup(*sites);
212 Curl_llist_destroy(new_list, NULL);
213 return CURLM_OUT_OF_MEMORY;
216 entry = malloc(sizeof(struct site_blacklist_entry));
219 Curl_llist_destroy(new_list, NULL);
220 return CURLM_OUT_OF_MEMORY;
223 port = strchr(hostname, ':');
227 entry->port = (unsigned short)strtol(port, NULL, 10);
230 /* Default port number for HTTP */
234 entry->hostname = hostname;
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;
246 /* Free the old list */
248 Curl_llist_destroy(old_list, NULL);
251 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
252 *list_ptr = new_list;
257 bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle,
260 if(handle->multi && server_name) {
261 struct curl_llist *blacklist =
262 Curl_multi_pipelining_server_bl(handle->multi);
265 struct curl_llist_element *curr;
267 curr = blacklist->head;
269 char *bl_server_name;
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);
281 DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name));
286 CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
287 struct curl_llist **list_ptr)
289 struct curl_llist *old_list = *list_ptr;
290 struct curl_llist *new_list = NULL;
293 new_list = Curl_llist_alloc((curl_llist_dtor) server_blacklist_llist_dtor);
295 return CURLM_OUT_OF_MEMORY;
297 /* Parse the URLs and populate the list */
301 server_name = strdup(*servers);
303 return CURLM_OUT_OF_MEMORY;
305 if(!Curl_llist_insert_next(new_list, new_list->tail, server_name))
306 return CURLM_OUT_OF_MEMORY;
312 /* Free the old list */
314 Curl_llist_destroy(old_list, NULL);
317 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
318 *list_ptr = new_list;
323 static bool pipe_head(struct SessionHandle *data,
324 struct curl_llist *pipeline)
327 struct curl_llist_element *curr = pipeline->head;
329 return (curr->ptr == data) ? TRUE : FALSE;
334 /* returns TRUE if the given handle is head of the recv pipe */
335 bool Curl_recvpipe_head(struct SessionHandle *data,
336 struct connectdata *conn)
338 return pipe_head(data, conn->recv_pipe);
341 /* returns TRUE if the given handle is head of the send pipe */
342 bool Curl_sendpipe_head(struct SessionHandle *data,
343 struct connectdata *conn)
345 return pipe_head(data, conn->send_pipe);
350 * Check if the write channel is available and this handle as at the head,
351 * then grab the channel and return TRUE.
353 * If not available, return FALSE.
356 bool Curl_pipeline_checkget_write(struct SessionHandle *data,
357 struct connectdata *conn)
359 if(conn->bits.multiplex)
360 /* when multiplexing, we can use it at once */
363 if(!conn->writechannel_inuse && Curl_sendpipe_head(data, conn)) {
364 /* Grab the channel */
365 conn->writechannel_inuse = TRUE;
373 * Check if the read channel is available and this handle as at the head, then
374 * grab the channel and return TRUE.
376 * If not available, return FALSE.
379 bool Curl_pipeline_checkget_read(struct SessionHandle *data,
380 struct connectdata *conn)
382 if(conn->bits.multiplex)
383 /* when multiplexing, we can use it at once */
386 if(!conn->readchannel_inuse && Curl_recvpipe_head(data, conn)) {
387 /* Grab the channel */
388 conn->readchannel_inuse = TRUE;
395 * The current user of the pipeline write channel gives it up.
397 void Curl_pipeline_leave_write(struct connectdata *conn)
399 conn->writechannel_inuse = FALSE;
403 * The current user of the pipeline read channel gives it up.
405 void Curl_pipeline_leave_read(struct connectdata *conn)
407 conn->readchannel_inuse = FALSE;
412 void print_pipeline(struct connectdata *conn)
414 struct curl_llist_element *curr;
415 struct connectbundle *cb_ptr;
416 struct SessionHandle *data = conn->data;
418 cb_ptr = conn->bundle;
421 curr = cb_ptr->conn_list->head;
424 infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n",
427 conn->send_pipe->size,
428 conn->recv_pipe->size);