• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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