blob: 4d41b04139b0bb5206c0ad3b49cb91de2876b4c8 [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 Hughes82be86d2017-09-20 17:00:17 -07009 * Copyright (C) 2013 - 2017, 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: (%"
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");
88 return penalized;
89 }
90 return FALSE;
91}
92
Alex Deymoe3149cc2016-10-05 11:18:42 -070093static CURLcode addHandleToPipeline(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070094 struct curl_llist *pipeline)
95{
Elliott Hughes82be86d2017-09-20 17:00:17 -070096 Curl_llist_insert_next(pipeline, pipeline->tail, data,
97 &data->pipeline_queue);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070098 return CURLE_OK;
99}
100
101
Alex Deymoe3149cc2016-10-05 11:18:42 -0700102CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700103 struct connectdata *conn)
104{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700105 struct curl_llist_element *sendhead = conn->send_pipe.head;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700106 struct curl_llist *pipeline;
107 CURLcode result;
108
Elliott Hughes82be86d2017-09-20 17:00:17 -0700109 pipeline = &conn->send_pipe;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700110
111 result = addHandleToPipeline(handle, pipeline);
112
Elliott Hughes82be86d2017-09-20 17:00:17 -0700113 if(pipeline == &conn->send_pipe && sendhead != conn->send_pipe.head) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700114 /* this is a new one as head, expire it */
115 Curl_pipeline_leave_write(conn); /* not in use yet */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700116 Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700117 }
118
119#if 0 /* enable for pipeline debugging */
120 print_pipeline(conn);
121#endif
122
123 return result;
124}
125
126/* Move this transfer from the sending list to the receiving list.
127
128 Pay special attention to the new sending list "leader" as it needs to get
129 checked to update what sockets it acts on.
130
131*/
Alex Deymoe3149cc2016-10-05 11:18:42 -0700132void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *handle,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700133 struct connectdata *conn)
134{
135 struct curl_llist_element *curr;
136
Elliott Hughes82be86d2017-09-20 17:00:17 -0700137 curr = conn->send_pipe.head;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700138 while(curr) {
139 if(curr->ptr == handle) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700140 Curl_llist_move(&conn->send_pipe, curr,
141 &conn->recv_pipe, conn->recv_pipe.tail);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700142
Elliott Hughes82be86d2017-09-20 17:00:17 -0700143 if(conn->send_pipe.head) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700144 /* 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 */
147#ifdef DEBUGBUILD
148 infof(conn->data, "%p is at send pipe head B!\n",
Elliott Hughes82be86d2017-09-20 17:00:17 -0700149 (void *)conn->send_pipe.head->ptr);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700150#endif
Elliott Hughes82be86d2017-09-20 17:00:17 -0700151 Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700152 }
153
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 */
157
158 break; /* we're done! */
159 }
160 curr = curr->next;
161 }
162}
163
Alex Deymoe3149cc2016-10-05 11:18:42 -0700164bool Curl_pipeline_site_blacklisted(struct Curl_easy *handle,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700165 struct connectdata *conn)
166{
167 if(handle->multi) {
168 struct curl_llist *blacklist =
169 Curl_multi_pipelining_site_bl(handle->multi);
170
171 if(blacklist) {
172 struct curl_llist_element *curr;
173
174 curr = blacklist->head;
175 while(curr) {
176 struct site_blacklist_entry *site;
177
178 site = curr->ptr;
Elliott Hughescee03382017-06-23 12:17:18 -0700179 if(strcasecompare(site->hostname, conn->host.name) &&
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700180 site->port == conn->remote_port) {
181 infof(handle, "Site %s:%d is pipeline blacklisted\n",
182 conn->host.name, conn->remote_port);
183 return TRUE;
184 }
185 curr = curr->next;
186 }
187 }
188 }
189 return FALSE;
190}
191
192CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
Elliott Hughes82be86d2017-09-20 17:00:17 -0700193 struct curl_llist *list)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700194{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700195 /* Free the old list */
196 if(list->size)
197 Curl_llist_destroy(list, NULL);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700198
199 if(sites) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700200 Curl_llist_init(list, (curl_llist_dtor) site_blacklist_llist_dtor);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700201
202 /* Parse the URLs and populate the list */
203 while(*sites) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700204 char *port;
205 struct site_blacklist_entry *entry;
206
Elliott Hughes82be86d2017-09-20 17:00:17 -0700207 entry = malloc(sizeof(struct site_blacklist_entry) + strlen(*sites));
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700208 if(!entry) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700209 Curl_llist_destroy(list, NULL);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700210 return CURLM_OUT_OF_MEMORY;
211 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700212 strcpy(entry->hostname, *sites);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700213
Elliott Hughes82be86d2017-09-20 17:00:17 -0700214 port = strchr(entry->hostname, ':');
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700215 if(port) {
216 *port = '\0';
217 port++;
218 entry->port = (unsigned short)strtol(port, NULL, 10);
219 }
220 else {
221 /* Default port number for HTTP */
222 entry->port = 80;
223 }
224
Elliott Hughes82be86d2017-09-20 17:00:17 -0700225 Curl_llist_insert_next(list, list->tail, entry, &entry->list);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700226 sites++;
227 }
228 }
229
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700230 return CURLM_OK;
231}
232
Elliott Hughes82be86d2017-09-20 17:00:17 -0700233struct blacklist_node {
234 struct curl_llist_element list;
235 char server_name[1];
236};
237
Alex Deymoe3149cc2016-10-05 11:18:42 -0700238bool Curl_pipeline_server_blacklisted(struct Curl_easy *handle,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700239 char *server_name)
240{
241 if(handle->multi && server_name) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700242 struct curl_llist *list =
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700243 Curl_multi_pipelining_server_bl(handle->multi);
244
Elliott Hughes82be86d2017-09-20 17:00:17 -0700245 struct curl_llist_element *e = list->head;
246 while(e) {
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);
251 return TRUE;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700252 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700253 e = e->next;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700254 }
255
256 DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name));
257 }
258 return FALSE;
259}
260
261CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
Elliott Hughes82be86d2017-09-20 17:00:17 -0700262 struct curl_llist *list)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700263{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700264 /* Free the old list */
265 if(list->size)
266 Curl_llist_destroy(list, NULL);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700267
268 if(servers) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700269 Curl_llist_init(list, (curl_llist_dtor) server_blacklist_llist_dtor);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700270
271 /* Parse the URLs and populate the list */
272 while(*servers) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700273 struct blacklist_node *n;
274 size_t len = strlen(*servers);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700275
Elliott Hughes82be86d2017-09-20 17:00:17 -0700276 n = malloc(sizeof(struct blacklist_node) + len);
277 if(!n) {
278 Curl_llist_destroy(list, NULL);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700279 return CURLM_OUT_OF_MEMORY;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700280 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700281 strcpy(n->server_name, *servers);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700282
Elliott Hughes82be86d2017-09-20 17:00:17 -0700283 Curl_llist_insert_next(list, list->tail, n, &n->list);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700284 servers++;
285 }
286 }
287
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700288
289 return CURLM_OK;
290}
291
Alex Deymoe3149cc2016-10-05 11:18:42 -0700292static bool pipe_head(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700293 struct curl_llist *pipeline)
294{
Alex Deymod15eaac2016-06-28 14:49:26 -0700295 if(pipeline) {
296 struct curl_llist_element *curr = pipeline->head;
297 if(curr)
298 return (curr->ptr == data) ? TRUE : FALSE;
299 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700300 return FALSE;
301}
302
303/* returns TRUE if the given handle is head of the recv pipe */
Alex Deymoe3149cc2016-10-05 11:18:42 -0700304bool Curl_recvpipe_head(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700305 struct connectdata *conn)
306{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700307 return pipe_head(data, &conn->recv_pipe);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700308}
309
310/* returns TRUE if the given handle is head of the send pipe */
Alex Deymoe3149cc2016-10-05 11:18:42 -0700311bool Curl_sendpipe_head(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700312 struct connectdata *conn)
313{
Elliott Hughes82be86d2017-09-20 17:00:17 -0700314 return pipe_head(data, &conn->send_pipe);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700315}
316
317
318/*
319 * Check if the write channel is available and this handle as at the head,
320 * then grab the channel and return TRUE.
321 *
322 * If not available, return FALSE.
323 */
324
Alex Deymoe3149cc2016-10-05 11:18:42 -0700325bool Curl_pipeline_checkget_write(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700326 struct connectdata *conn)
327{
328 if(conn->bits.multiplex)
329 /* when multiplexing, we can use it at once */
330 return TRUE;
331
332 if(!conn->writechannel_inuse && Curl_sendpipe_head(data, conn)) {
333 /* Grab the channel */
334 conn->writechannel_inuse = TRUE;
335 return TRUE;
336 }
337 return FALSE;
338}
339
340
341/*
342 * Check if the read channel is available and this handle as at the head, then
343 * grab the channel and return TRUE.
344 *
345 * If not available, return FALSE.
346 */
347
Alex Deymoe3149cc2016-10-05 11:18:42 -0700348bool Curl_pipeline_checkget_read(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700349 struct connectdata *conn)
350{
351 if(conn->bits.multiplex)
352 /* when multiplexing, we can use it at once */
353 return TRUE;
354
355 if(!conn->readchannel_inuse && Curl_recvpipe_head(data, conn)) {
356 /* Grab the channel */
357 conn->readchannel_inuse = TRUE;
358 return TRUE;
359 }
360 return FALSE;
361}
362
363/*
364 * The current user of the pipeline write channel gives it up.
365 */
366void Curl_pipeline_leave_write(struct connectdata *conn)
367{
368 conn->writechannel_inuse = FALSE;
369}
370
371/*
372 * The current user of the pipeline read channel gives it up.
373 */
374void Curl_pipeline_leave_read(struct connectdata *conn)
375{
376 conn->readchannel_inuse = FALSE;
377}
378
379
380#if 0
381void print_pipeline(struct connectdata *conn)
382{
383 struct curl_llist_element *curr;
384 struct connectbundle *cb_ptr;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700385 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700386
387 cb_ptr = conn->bundle;
388
389 if(cb_ptr) {
390 curr = cb_ptr->conn_list->head;
391 while(curr) {
392 conn = curr->ptr;
393 infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n",
394 conn->connection_id,
395 (void *)conn,
396 conn->send_pipe->size,
397 conn->recv_pipe->size);
398 curr = curr->next;
399 }
400 }
401}
402
403#endif