blob: 8de3babd781c1f678e704cec6787e5fb6ab7f34f [file] [log] [blame]
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07009 * Copyright (C) 2013 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070010 *
11 * This software is licensed as described in the file COPYING, which
12 * you should have received as part of this distribution. The terms
Alex Deymod15eaac2016-06-28 14:49:26 -070013 * are also available at https://curl.haxx.se/docs/copyright.html.
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070014 *
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"
Elliott Hughescee03382017-06-23 12:17:18 -070034#include "strcase.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070035
36#include "curl_memory.h"
37/* The last #include file should be: */
38#include "memdebug.h"
39
40struct site_blacklist_entry {
Elliott Hughes82be86d2017-09-20 17:00:17 -070041 struct curl_llist_element list;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070042 unsigned short port;
Elliott Hughes82be86d2017-09-20 17:00:17 -070043 char hostname[1];
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070044};
45
46static void site_blacklist_llist_dtor(void *user, void *element)
47{
48 struct site_blacklist_entry *entry = element;
49 (void)user;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070050 free(entry);
51}
52
53static void server_blacklist_llist_dtor(void *user, void *element)
54{
55 (void)user;
56 free(element);
57}
58
Alex Deymoe3149cc2016-10-05 11:18:42 -070059bool Curl_pipeline_penalized(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070060 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 */
Elliott Hughes82be86d2017-09-20 17:00:17 -070071 if(conn->recv_pipe.head) {
72 struct Curl_easy *recv_handle = conn->recv_pipe.head->ptr;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070073
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: (%"
Elliott Hughes72d948d2018-08-03 14:37:21 -070085 CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T
86 "), penalized: %s\n",
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070087 conn->connection_id, (void *)conn, recv_size,
88 conn->chunk.datasize, penalized?"TRUE":"FALSE");
89 return penalized;
90 }
91 return FALSE;
92}
93
Alex Deymoe3149cc2016-10-05 11:18:42 -070094static CURLcode addHandleToPipeline(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070095 struct curl_llist *pipeline)
96{
Elliott Hughes82be86d2017-09-20 17:00:17 -070097 Curl_llist_insert_next(pipeline, pipeline->tail, data,
98 &data->pipeline_queue);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070099 return CURLE_OK;
100}
101
102
Alex Deymoe3149cc2016-10-05 11:18:42 -0700103CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700104 struct connectdata *conn)
105{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700106 struct curl_llist_element *sendhead = conn->send_pipe.head;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700107 struct curl_llist *pipeline;
108 CURLcode result;
109
Elliott Hughes82be86d2017-09-20 17:00:17 -0700110 pipeline = &conn->send_pipe;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700111
112 result = addHandleToPipeline(handle, pipeline);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -0700113 if((conn->bundle->multiuse == BUNDLE_PIPELINING) &&
114 (pipeline == &conn->send_pipe && sendhead != conn->send_pipe.head)) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700115 /* this is a new one as head, expire it */
116 Curl_pipeline_leave_write(conn); /* not in use yet */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700117 Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700118 }
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*/
Alex Deymoe3149cc2016-10-05 11:18:42 -0700133void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *handle,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700134 struct connectdata *conn)
135{
136 struct curl_llist_element *curr;
137
Elliott Hughes82be86d2017-09-20 17:00:17 -0700138 curr = conn->send_pipe.head;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700139 while(curr) {
140 if(curr->ptr == handle) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700141 Curl_llist_move(&conn->send_pipe, curr,
142 &conn->recv_pipe, conn->recv_pipe.tail);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700143
Elliott Hughes82be86d2017-09-20 17:00:17 -0700144 if(conn->send_pipe.head) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700145 /* 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",
Elliott Hughes82be86d2017-09-20 17:00:17 -0700150 (void *)conn->send_pipe.head->ptr);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700151#endif
Elliott Hughes82be86d2017-09-20 17:00:17 -0700152 Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700153 }
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
Alex Deymoe3149cc2016-10-05 11:18:42 -0700165bool Curl_pipeline_site_blacklisted(struct Curl_easy *handle,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700166 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;
Elliott Hughescee03382017-06-23 12:17:18 -0700180 if(strcasecompare(site->hostname, conn->host.name) &&
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700181 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
193CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
Elliott Hughes82be86d2017-09-20 17:00:17 -0700194 struct curl_llist *list)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700195{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700196 /* Free the old list */
197 if(list->size)
198 Curl_llist_destroy(list, NULL);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700199
200 if(sites) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700201 Curl_llist_init(list, (curl_llist_dtor) site_blacklist_llist_dtor);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700202
203 /* Parse the URLs and populate the list */
204 while(*sites) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700205 char *port;
206 struct site_blacklist_entry *entry;
207
Elliott Hughes82be86d2017-09-20 17:00:17 -0700208 entry = malloc(sizeof(struct site_blacklist_entry) + strlen(*sites));
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700209 if(!entry) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700210 Curl_llist_destroy(list, NULL);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700211 return CURLM_OUT_OF_MEMORY;
212 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700213 strcpy(entry->hostname, *sites);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700214
Elliott Hughes82be86d2017-09-20 17:00:17 -0700215 port = strchr(entry->hostname, ':');
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700216 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
Elliott Hughes82be86d2017-09-20 17:00:17 -0700226 Curl_llist_insert_next(list, list->tail, entry, &entry->list);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700227 sites++;
228 }
229 }
230
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700231 return CURLM_OK;
232}
233
Elliott Hughes82be86d2017-09-20 17:00:17 -0700234struct blacklist_node {
235 struct curl_llist_element list;
236 char server_name[1];
237};
238
Alex Deymoe3149cc2016-10-05 11:18:42 -0700239bool Curl_pipeline_server_blacklisted(struct Curl_easy *handle,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700240 char *server_name)
241{
242 if(handle->multi && server_name) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700243 struct curl_llist *list =
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700244 Curl_multi_pipelining_server_bl(handle->multi);
245
Elliott Hughes82be86d2017-09-20 17:00:17 -0700246 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;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700253 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700254 e = e->next;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700255 }
256
257 DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name));
258 }
259 return FALSE;
260}
261
262CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
Elliott Hughes82be86d2017-09-20 17:00:17 -0700263 struct curl_llist *list)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700264{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700265 /* Free the old list */
266 if(list->size)
267 Curl_llist_destroy(list, NULL);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700268
269 if(servers) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700270 Curl_llist_init(list, (curl_llist_dtor) server_blacklist_llist_dtor);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700271
272 /* Parse the URLs and populate the list */
273 while(*servers) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700274 struct blacklist_node *n;
275 size_t len = strlen(*servers);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700276
Elliott Hughes82be86d2017-09-20 17:00:17 -0700277 n = malloc(sizeof(struct blacklist_node) + len);
278 if(!n) {
279 Curl_llist_destroy(list, NULL);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700280 return CURLM_OUT_OF_MEMORY;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700281 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700282 strcpy(n->server_name, *servers);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700283
Elliott Hughes82be86d2017-09-20 17:00:17 -0700284 Curl_llist_insert_next(list, list->tail, n, &n->list);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700285 servers++;
286 }
287 }
288
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700289
290 return CURLM_OK;
291}
292
Alex Deymoe3149cc2016-10-05 11:18:42 -0700293static bool pipe_head(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700294 struct curl_llist *pipeline)
295{
Alex Deymod15eaac2016-06-28 14:49:26 -0700296 if(pipeline) {
297 struct curl_llist_element *curr = pipeline->head;
298 if(curr)
299 return (curr->ptr == data) ? TRUE : FALSE;
300 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700301 return FALSE;
302}
303
304/* returns TRUE if the given handle is head of the recv pipe */
Alex Deymoe3149cc2016-10-05 11:18:42 -0700305bool Curl_recvpipe_head(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700306 struct connectdata *conn)
307{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700308 return pipe_head(data, &conn->recv_pipe);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700309}
310
311/* returns TRUE if the given handle is head of the send pipe */
Alex Deymoe3149cc2016-10-05 11:18:42 -0700312bool Curl_sendpipe_head(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700313 struct connectdata *conn)
314{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700315 return pipe_head(data, &conn->send_pipe);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700316}
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
Alex Deymoe3149cc2016-10-05 11:18:42 -0700326bool Curl_pipeline_checkget_write(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700327 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
Alex Deymoe3149cc2016-10-05 11:18:42 -0700349bool Curl_pipeline_checkget_read(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700350 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 */
367void 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 */
375void Curl_pipeline_leave_read(struct connectdata *conn)
376{
377 conn->readchannel_inuse = FALSE;
378}
379
380
381#if 0
382void print_pipeline(struct connectdata *conn)
383{
384 struct curl_llist_element *curr;
385 struct connectbundle *cb_ptr;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700386 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700387
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