1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2013-2015, Daniel Stenberg, <daniel@haxx.se>, et al.
10 *
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 http://curl.haxx.se/docs/copyright.html.
14 *
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"
34 #include "rawstr.h"
35
36 #include "curl_memory.h"
37 /* The last #include file should be: */
38 #include "memdebug.h"
39
40 struct site_blacklist_entry {
41 char *hostname;
42 unsigned short port;
43 };
44
site_blacklist_llist_dtor(void * user,void * element)45 static void site_blacklist_llist_dtor(void *user, void *element)
46 {
47 struct site_blacklist_entry *entry = element;
48 (void)user;
49
50 Curl_safefree(entry->hostname);
51 free(entry);
52 }
53
server_blacklist_llist_dtor(void * user,void * element)54 static void server_blacklist_llist_dtor(void *user, void *element)
55 {
56 (void)user;
57 free(element);
58 }
59
Curl_pipeline_penalized(struct SessionHandle * data,struct connectdata * conn)60 bool Curl_pipeline_penalized(struct SessionHandle *data,
61 struct connectdata *conn)
62 {
63 if(data) {
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 */
70
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;
74
75 recv_size = recv_handle->req.size;
76
77 if(penalty_size > 0 && recv_size > penalty_size)
78 penalized = TRUE;
79 }
80
81 if(chunk_penalty_size > 0 &&
82 (curl_off_t)conn->chunk.datasize > chunk_penalty_size)
83 penalized = TRUE;
84
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");
89 return penalized;
90 }
91 return FALSE;
92 }
93
addHandleToPipeline(struct SessionHandle * data,struct curl_llist * pipeline)94 static CURLcode addHandleToPipeline(struct SessionHandle *data,
95 struct curl_llist *pipeline)
96 {
97 if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
98 return CURLE_OUT_OF_MEMORY;
99 return CURLE_OK;
100 }
101
102
Curl_add_handle_to_pipeline(struct SessionHandle * handle,struct connectdata * conn)103 CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle,
104 struct connectdata *conn)
105 {
106 struct curl_llist_element *sendhead = conn->send_pipe->head;
107 struct curl_llist *pipeline;
108 CURLcode result;
109
110 pipeline = conn->send_pipe;
111
112 result = addHandleToPipeline(handle, pipeline);
113
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);
118 }
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 */
Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle * handle,struct connectdata * conn)133 void Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle *handle,
134 struct connectdata *conn)
135 {
136 struct curl_llist_element *curr;
137
138 curr = conn->send_pipe->head;
139 while(curr) {
140 if(curr->ptr == handle) {
141 Curl_llist_move(conn->send_pipe, curr,
142 conn->recv_pipe, conn->recv_pipe->tail);
143
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 */
148 #ifdef DEBUGBUILD
149 infof(conn->data, "%p is at send pipe head B!\n",
150 (void *)conn->send_pipe->head->ptr);
151 #endif
152 Curl_expire(conn->send_pipe->head->ptr, 1);
153 }
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
Curl_pipeline_site_blacklisted(struct SessionHandle * handle,struct connectdata * conn)165 bool Curl_pipeline_site_blacklisted(struct SessionHandle *handle,
166 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;
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);
184 return TRUE;
185 }
186 curr = curr->next;
187 }
188 }
189 }
190 return FALSE;
191 }
192
Curl_pipeline_set_site_blacklist(char ** sites,struct curl_llist ** list_ptr)193 CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
194 struct curl_llist **list_ptr)
195 {
196 struct curl_llist *old_list = *list_ptr;
197 struct curl_llist *new_list = NULL;
198
199 if(sites) {
200 new_list = Curl_llist_alloc((curl_llist_dtor) site_blacklist_llist_dtor);
201 if(!new_list)
202 return CURLM_OUT_OF_MEMORY;
203
204 /* Parse the URLs and populate the list */
205 while(*sites) {
206 char *hostname;
207 char *port;
208 struct site_blacklist_entry *entry;
209
210 hostname = strdup(*sites);
211 if(!hostname) {
212 Curl_llist_destroy(new_list, NULL);
213 return CURLM_OUT_OF_MEMORY;
214 }
215
216 entry = malloc(sizeof(struct site_blacklist_entry));
217 if(!entry) {
218 free(hostname);
219 Curl_llist_destroy(new_list, NULL);
220 return CURLM_OUT_OF_MEMORY;
221 }
222
223 port = strchr(hostname, ':');
224 if(port) {
225 *port = '\0';
226 port++;
227 entry->port = (unsigned short)strtol(port, NULL, 10);
228 }
229 else {
230 /* Default port number for HTTP */
231 entry->port = 80;
232 }
233
234 entry->hostname = hostname;
235
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;
240 }
241
242 sites++;
243 }
244 }
245
246 /* Free the old list */
247 if(old_list) {
248 Curl_llist_destroy(old_list, NULL);
249 }
250
251 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
252 *list_ptr = new_list;
253
254 return CURLM_OK;
255 }
256
Curl_pipeline_server_blacklisted(struct SessionHandle * handle,char * server_name)257 bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle,
258 char *server_name)
259 {
260 if(handle->multi && server_name) {
261 struct curl_llist *blacklist =
262 Curl_multi_pipelining_server_bl(handle->multi);
263
264 if(blacklist) {
265 struct curl_llist_element *curr;
266
267 curr = blacklist->head;
268 while(curr) {
269 char *bl_server_name;
270
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);
275 return TRUE;
276 }
277 curr = curr->next;
278 }
279 }
280
281 DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name));
282 }
283 return FALSE;
284 }
285
Curl_pipeline_set_server_blacklist(char ** servers,struct curl_llist ** list_ptr)286 CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
287 struct curl_llist **list_ptr)
288 {
289 struct curl_llist *old_list = *list_ptr;
290 struct curl_llist *new_list = NULL;
291
292 if(servers) {
293 new_list = Curl_llist_alloc((curl_llist_dtor) server_blacklist_llist_dtor);
294 if(!new_list)
295 return CURLM_OUT_OF_MEMORY;
296
297 /* Parse the URLs and populate the list */
298 while(*servers) {
299 char *server_name;
300
301 server_name = strdup(*servers);
302 if(!server_name)
303 return CURLM_OUT_OF_MEMORY;
304
305 if(!Curl_llist_insert_next(new_list, new_list->tail, server_name))
306 return CURLM_OUT_OF_MEMORY;
307
308 servers++;
309 }
310 }
311
312 /* Free the old list */
313 if(old_list) {
314 Curl_llist_destroy(old_list, NULL);
315 }
316
317 /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
318 *list_ptr = new_list;
319
320 return CURLM_OK;
321 }
322
pipe_head(struct SessionHandle * data,struct curl_llist * pipeline)323 static bool pipe_head(struct SessionHandle *data,
324 struct curl_llist *pipeline)
325 {
326 struct curl_llist_element *curr = pipeline->head;
327 if(curr)
328 return (curr->ptr == data) ? TRUE : FALSE;
329
330 return FALSE;
331 }
332
333 /* returns TRUE if the given handle is head of the recv pipe */
Curl_recvpipe_head(struct SessionHandle * data,struct connectdata * conn)334 bool Curl_recvpipe_head(struct SessionHandle *data,
335 struct connectdata *conn)
336 {
337 return pipe_head(data, conn->recv_pipe);
338 }
339
340 /* returns TRUE if the given handle is head of the send pipe */
Curl_sendpipe_head(struct SessionHandle * data,struct connectdata * conn)341 bool Curl_sendpipe_head(struct SessionHandle *data,
342 struct connectdata *conn)
343 {
344 return pipe_head(data, conn->send_pipe);
345 }
346
347
348 /*
349 * Check if the write channel is available and this handle as at the head,
350 * then grab the channel and return TRUE.
351 *
352 * If not available, return FALSE.
353 */
354
Curl_pipeline_checkget_write(struct SessionHandle * data,struct connectdata * conn)355 bool Curl_pipeline_checkget_write(struct SessionHandle *data,
356 struct connectdata *conn)
357 {
358 if(conn->bits.multiplex)
359 /* when multiplexing, we can use it at once */
360 return TRUE;
361
362 if(!conn->writechannel_inuse && Curl_sendpipe_head(data, conn)) {
363 /* Grab the channel */
364 conn->writechannel_inuse = TRUE;
365 return TRUE;
366 }
367 return FALSE;
368 }
369
370
371 /*
372 * Check if the read channel is available and this handle as at the head, then
373 * grab the channel and return TRUE.
374 *
375 * If not available, return FALSE.
376 */
377
Curl_pipeline_checkget_read(struct SessionHandle * data,struct connectdata * conn)378 bool Curl_pipeline_checkget_read(struct SessionHandle *data,
379 struct connectdata *conn)
380 {
381 if(conn->bits.multiplex)
382 /* when multiplexing, we can use it at once */
383 return TRUE;
384
385 if(!conn->readchannel_inuse && Curl_recvpipe_head(data, conn)) {
386 /* Grab the channel */
387 conn->readchannel_inuse = TRUE;
388 return TRUE;
389 }
390 return FALSE;
391 }
392
393 /*
394 * The current user of the pipeline write channel gives it up.
395 */
Curl_pipeline_leave_write(struct connectdata * conn)396 void Curl_pipeline_leave_write(struct connectdata *conn)
397 {
398 conn->writechannel_inuse = FALSE;
399 }
400
401 /*
402 * The current user of the pipeline read channel gives it up.
403 */
Curl_pipeline_leave_read(struct connectdata * conn)404 void Curl_pipeline_leave_read(struct connectdata *conn)
405 {
406 conn->readchannel_inuse = FALSE;
407 }
408
409
410 #if 0
411 void print_pipeline(struct connectdata *conn)
412 {
413 struct curl_llist_element *curr;
414 struct connectbundle *cb_ptr;
415 struct SessionHandle *data = conn->data;
416
417 cb_ptr = conn->bundle;
418
419 if(cb_ptr) {
420 curr = cb_ptr->conn_list->head;
421 while(curr) {
422 conn = curr->ptr;
423 infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n",
424 conn->connection_id,
425 (void *)conn,
426 conn->send_pipe->size,
427 conn->recv_pipe->size);
428 curr = curr->next;
429 }
430 }
431 }
432
433 #endif
434