Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 2013, Linus Nielsen Feltzing, <linus (at) haxx.se>
      9  * Copyright (C) 2013-2015, Daniel Stenberg, <daniel (at) 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 https://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 
     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 
     54 static void server_blacklist_llist_dtor(void *user, void *element)
     55 {
     56   (void)user;
     57   free(element);
     58 }
     59 
     60 bool Curl_pipeline_penalized(struct Curl_easy *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 Curl_easy *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 
     94 static CURLcode addHandleToPipeline(struct Curl_easy *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 
    103 CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *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 */
    133 void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *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 
    165 bool Curl_pipeline_site_blacklisted(struct Curl_easy *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 
    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 
    257 bool Curl_pipeline_server_blacklisted(struct Curl_easy *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 
    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         Curl_llist_destroy(new_list, NULL);
    304         return CURLM_OUT_OF_MEMORY;
    305       }
    306 
    307       if(!Curl_llist_insert_next(new_list, new_list->tail, server_name)) {
    308         Curl_llist_destroy(new_list, NULL);
    309         Curl_safefree(server_name);
    310         return CURLM_OUT_OF_MEMORY;
    311       }
    312 
    313       servers++;
    314     }
    315   }
    316 
    317   /* Free the old list */
    318   if(old_list) {
    319     Curl_llist_destroy(old_list, NULL);
    320   }
    321 
    322   /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
    323   *list_ptr = new_list;
    324 
    325   return CURLM_OK;
    326 }
    327 
    328 static bool pipe_head(struct Curl_easy *data,
    329                       struct curl_llist *pipeline)
    330 {
    331   if(pipeline) {
    332     struct curl_llist_element *curr = pipeline->head;
    333     if(curr)
    334       return (curr->ptr == data) ? TRUE : FALSE;
    335   }
    336   return FALSE;
    337 }
    338 
    339 /* returns TRUE if the given handle is head of the recv pipe */
    340 bool Curl_recvpipe_head(struct Curl_easy *data,
    341                         struct connectdata *conn)
    342 {
    343   return pipe_head(data, conn->recv_pipe);
    344 }
    345 
    346 /* returns TRUE if the given handle is head of the send pipe */
    347 bool Curl_sendpipe_head(struct Curl_easy *data,
    348                         struct connectdata *conn)
    349 {
    350   return pipe_head(data, conn->send_pipe);
    351 }
    352 
    353 
    354 /*
    355  * Check if the write channel is available and this handle as at the head,
    356  * then grab the channel and return TRUE.
    357  *
    358  * If not available, return FALSE.
    359  */
    360 
    361 bool Curl_pipeline_checkget_write(struct Curl_easy *data,
    362                                   struct connectdata *conn)
    363 {
    364   if(conn->bits.multiplex)
    365     /* when multiplexing, we can use it at once */
    366     return TRUE;
    367 
    368   if(!conn->writechannel_inuse && Curl_sendpipe_head(data, conn)) {
    369     /* Grab the channel */
    370     conn->writechannel_inuse = TRUE;
    371     return TRUE;
    372   }
    373   return FALSE;
    374 }
    375 
    376 
    377 /*
    378  * Check if the read channel is available and this handle as at the head, then
    379  * grab the channel and return TRUE.
    380  *
    381  * If not available, return FALSE.
    382  */
    383 
    384 bool Curl_pipeline_checkget_read(struct Curl_easy *data,
    385                                  struct connectdata *conn)
    386 {
    387   if(conn->bits.multiplex)
    388     /* when multiplexing, we can use it at once */
    389     return TRUE;
    390 
    391   if(!conn->readchannel_inuse && Curl_recvpipe_head(data, conn)) {
    392     /* Grab the channel */
    393     conn->readchannel_inuse = TRUE;
    394     return TRUE;
    395   }
    396   return FALSE;
    397 }
    398 
    399 /*
    400  * The current user of the pipeline write channel gives it up.
    401  */
    402 void Curl_pipeline_leave_write(struct connectdata *conn)
    403 {
    404   conn->writechannel_inuse = FALSE;
    405 }
    406 
    407 /*
    408  * The current user of the pipeline read channel gives it up.
    409  */
    410 void Curl_pipeline_leave_read(struct connectdata *conn)
    411 {
    412   conn->readchannel_inuse = FALSE;
    413 }
    414 
    415 
    416 #if 0
    417 void print_pipeline(struct connectdata *conn)
    418 {
    419   struct curl_llist_element *curr;
    420   struct connectbundle *cb_ptr;
    421   struct Curl_easy *data = conn->data;
    422 
    423   cb_ptr = conn->bundle;
    424 
    425   if(cb_ptr) {
    426     curr = cb_ptr->conn_list->head;
    427     while(curr) {
    428       conn = curr->ptr;
    429       infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n",
    430             conn->connection_id,
    431             (void *)conn,
    432             conn->send_pipe->size,
    433             conn->recv_pipe->size);
    434       curr = curr->next;
    435     }
    436   }
    437 }
    438 
    439 #endif
    440