1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2013 - 2017, 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 {
41 struct curl_llist_element list;
46 static void site_blacklist_llist_dtor(void *user, void *element)
48 struct site_blacklist_entry *entry = element;
53 static void server_blacklist_llist_dtor(void *user, void *element)
59 bool Curl_pipeline_penalized(struct Curl_easy *data,
60 struct connectdata *conn)
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 */
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;
74 recv_size = recv_handle->req.size;
76 if(penalty_size > 0 && recv_size > penalty_size)
80 if(chunk_penalty_size > 0 &&
81 (curl_off_t)conn->chunk.datasize > chunk_penalty_size)
84 infof(data, "Conn: %ld (%p) Receive pipe weight: (%"
85 CURL_FORMAT_CURL_OFF_T "/%zu), penalized: %s\n",
86 conn->connection_id, (void *)conn, recv_size,
87 conn->chunk.datasize, penalized?"TRUE":"FALSE");
93 static CURLcode addHandleToPipeline(struct Curl_easy *data,
94 struct curl_llist *pipeline)
96 Curl_llist_insert_next(pipeline, pipeline->tail, data,
97 &data->pipeline_queue);
102 CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle,
103 struct connectdata *conn)
105 struct curl_llist_element *sendhead = conn->send_pipe.head;
106 struct curl_llist *pipeline;
109 pipeline = &conn->send_pipe;
111 result = addHandleToPipeline(handle, pipeline);
113 if(pipeline == &conn->send_pipe && sendhead != conn->send_pipe.head) {
114 /* this is a new one as head, expire it */
115 Curl_pipeline_leave_write(conn); /* not in use yet */
116 Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
119 #if 0 /* enable for pipeline debugging */
120 print_pipeline(conn);
126 /* Move this transfer from the sending list to the receiving list.
128 Pay special attention to the new sending list "leader" as it needs to get
129 checked to update what sockets it acts on.
132 void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *handle,
133 struct connectdata *conn)
135 struct curl_llist_element *curr;
137 curr = conn->send_pipe.head;
139 if(curr->ptr == handle) {
140 Curl_llist_move(&conn->send_pipe, curr,
141 &conn->recv_pipe, conn->recv_pipe.tail);
143 if(conn->send_pipe.head) {
144 /* Since there's a new easy handle at the start of the send pipeline,
145 set its timeout value to 1ms to make it trigger instantly */
146 Curl_pipeline_leave_write(conn); /* not used now */
148 infof(conn->data, "%p is at send pipe head B!\n",
149 (void *)conn->send_pipe.head->ptr);
151 Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
154 /* The receiver's list is not really interesting here since either this
155 handle is now first in the list and we'll deal with it soon, or
156 another handle is already first and thus is already taken care of */
158 break; /* we're done! */
164 bool Curl_pipeline_site_blacklisted(struct Curl_easy *handle,
165 struct connectdata *conn)
168 struct curl_llist *blacklist =
169 Curl_multi_pipelining_site_bl(handle->multi);
172 struct curl_llist_element *curr;
174 curr = blacklist->head;
176 struct site_blacklist_entry *site;
179 if(strcasecompare(site->hostname, conn->host.name) &&
180 site->port == conn->remote_port) {
181 infof(handle, "Site %s:%d is pipeline blacklisted\n",
182 conn->host.name, conn->remote_port);
192 CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
193 struct curl_llist *list)
195 /* Free the old list */
197 Curl_llist_destroy(list, NULL);
200 Curl_llist_init(list, (curl_llist_dtor) site_blacklist_llist_dtor);
202 /* Parse the URLs and populate the list */
205 struct site_blacklist_entry *entry;
207 entry = malloc(sizeof(struct site_blacklist_entry) + strlen(*sites));
209 Curl_llist_destroy(list, NULL);
210 return CURLM_OUT_OF_MEMORY;
212 strcpy(entry->hostname, *sites);
214 port = strchr(entry->hostname, ':');
218 entry->port = (unsigned short)strtol(port, NULL, 10);
221 /* Default port number for HTTP */
225 Curl_llist_insert_next(list, list->tail, entry, &entry->list);
233 struct blacklist_node {
234 struct curl_llist_element list;
238 bool Curl_pipeline_server_blacklisted(struct Curl_easy *handle,
241 if(handle->multi && server_name) {
242 struct curl_llist *list =
243 Curl_multi_pipelining_server_bl(handle->multi);
245 struct curl_llist_element *e = list->head;
247 struct blacklist_node *bl = (struct blacklist_node *)e;
248 if(strncasecompare(bl->server_name, server_name,
249 strlen(bl->server_name))) {
250 infof(handle, "Server %s is blacklisted\n", server_name);
256 DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name));
261 CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
262 struct curl_llist *list)
264 /* Free the old list */
266 Curl_llist_destroy(list, NULL);
269 Curl_llist_init(list, (curl_llist_dtor) server_blacklist_llist_dtor);
271 /* Parse the URLs and populate the list */
273 struct blacklist_node *n;
274 size_t len = strlen(*servers);
276 n = malloc(sizeof(struct blacklist_node) + len);
278 Curl_llist_destroy(list, NULL);
279 return CURLM_OUT_OF_MEMORY;
281 strcpy(n->server_name, *servers);
283 Curl_llist_insert_next(list, list->tail, n, &n->list);
292 static bool pipe_head(struct Curl_easy *data,
293 struct curl_llist *pipeline)
296 struct curl_llist_element *curr = pipeline->head;
298 return (curr->ptr == data) ? TRUE : FALSE;
303 /* returns TRUE if the given handle is head of the recv pipe */
304 bool Curl_recvpipe_head(struct Curl_easy *data,
305 struct connectdata *conn)
307 return pipe_head(data, &conn->recv_pipe);
310 /* returns TRUE if the given handle is head of the send pipe */
311 bool Curl_sendpipe_head(struct Curl_easy *data,
312 struct connectdata *conn)
314 return pipe_head(data, &conn->send_pipe);
319 * Check if the write channel is available and this handle as at the head,
320 * then grab the channel and return TRUE.
322 * If not available, return FALSE.
325 bool Curl_pipeline_checkget_write(struct Curl_easy *data,
326 struct connectdata *conn)
328 if(conn->bits.multiplex)
329 /* when multiplexing, we can use it at once */
332 if(!conn->writechannel_inuse && Curl_sendpipe_head(data, conn)) {
333 /* Grab the channel */
334 conn->writechannel_inuse = TRUE;
342 * Check if the read channel is available and this handle as at the head, then
343 * grab the channel and return TRUE.
345 * If not available, return FALSE.
348 bool Curl_pipeline_checkget_read(struct Curl_easy *data,
349 struct connectdata *conn)
351 if(conn->bits.multiplex)
352 /* when multiplexing, we can use it at once */
355 if(!conn->readchannel_inuse && Curl_recvpipe_head(data, conn)) {
356 /* Grab the channel */
357 conn->readchannel_inuse = TRUE;
364 * The current user of the pipeline write channel gives it up.
366 void Curl_pipeline_leave_write(struct connectdata *conn)
368 conn->writechannel_inuse = FALSE;
372 * The current user of the pipeline read channel gives it up.
374 void Curl_pipeline_leave_read(struct connectdata *conn)
376 conn->readchannel_inuse = FALSE;
381 void print_pipeline(struct connectdata *conn)
383 struct curl_llist_element *curr;
384 struct connectbundle *cb_ptr;
385 struct Curl_easy *data = conn->data;
387 cb_ptr = conn->bundle;
390 curr = cb_ptr->conn_list->head;
393 infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n",
396 conn->send_pipe->size,
397 conn->recv_pipe->size);