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 - 2018, 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 "strcase.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   struct curl_llist_element list;
     42   unsigned short port;
     43   char hostname[1];
     44 };
     45 
     46 static void site_blacklist_llist_dtor(void *user, void *element)
     47 {
     48   struct site_blacklist_entry *entry = element;
     49   (void)user;
     50   free(entry);
     51 }
     52 
     53 static void server_blacklist_llist_dtor(void *user, void *element)
     54 {
     55   (void)user;
     56   free(element);
     57 }
     58 
     59 bool Curl_pipeline_penalized(struct Curl_easy *data,
     60                              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 */
     71     if(conn->recv_pipe.head) {
     72       struct Curl_easy *recv_handle = conn->recv_pipe.head->ptr;
     73 
     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 "/%" CURL_FORMAT_CURL_OFF_T
     86           "), 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   Curl_llist_insert_next(pipeline, pipeline->tail, data,
     98                          &data->pipeline_queue);
     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   if((conn->bundle->multiuse == BUNDLE_PIPELINING) &&
    114      (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, 0, EXPIRE_RUN_NOW);
    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, 0, EXPIRE_RUN_NOW);
    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(strcasecompare(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)
    195 {
    196   /* Free the old list */
    197   if(list->size)
    198     Curl_llist_destroy(list, NULL);
    199 
    200   if(sites) {
    201     Curl_llist_init(list, (curl_llist_dtor) site_blacklist_llist_dtor);
    202 
    203     /* Parse the URLs and populate the list */
    204     while(*sites) {
    205       char *port;
    206       struct site_blacklist_entry *entry;
    207 
    208       entry = malloc(sizeof(struct site_blacklist_entry) + strlen(*sites));
    209       if(!entry) {
    210         Curl_llist_destroy(list, NULL);
    211         return CURLM_OUT_OF_MEMORY;
    212       }
    213       strcpy(entry->hostname, *sites);
    214 
    215       port = strchr(entry->hostname, ':');
    216       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 
    226       Curl_llist_insert_next(list, list->tail, entry, &entry->list);
    227       sites++;
    228     }
    229   }
    230 
    231   return CURLM_OK;
    232 }
    233 
    234 struct blacklist_node {
    235   struct curl_llist_element list;
    236   char server_name[1];
    237 };
    238 
    239 bool Curl_pipeline_server_blacklisted(struct Curl_easy *handle,
    240                                       char *server_name)
    241 {
    242   if(handle->multi && server_name) {
    243     struct curl_llist *list =
    244       Curl_multi_pipelining_server_bl(handle->multi);
    245 
    246     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;
    253       }
    254       e = e->next;
    255     }
    256 
    257     DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name));
    258   }
    259   return FALSE;
    260 }
    261 
    262 CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
    263                                              struct curl_llist *list)
    264 {
    265   /* Free the old list */
    266   if(list->size)
    267     Curl_llist_destroy(list, NULL);
    268 
    269   if(servers) {
    270     Curl_llist_init(list, (curl_llist_dtor) server_blacklist_llist_dtor);
    271 
    272     /* Parse the URLs and populate the list */
    273     while(*servers) {
    274       struct blacklist_node *n;
    275       size_t len = strlen(*servers);
    276 
    277       n = malloc(sizeof(struct blacklist_node) + len);
    278       if(!n) {
    279         Curl_llist_destroy(list, NULL);
    280         return CURLM_OUT_OF_MEMORY;
    281       }
    282       strcpy(n->server_name, *servers);
    283 
    284       Curl_llist_insert_next(list, list->tail, n, &n->list);
    285       servers++;
    286     }
    287   }
    288 
    289 
    290   return CURLM_OK;
    291 }
    292 
    293 static bool pipe_head(struct Curl_easy *data,
    294                       struct curl_llist *pipeline)
    295 {
    296   if(pipeline) {
    297     struct curl_llist_element *curr = pipeline->head;
    298     if(curr)
    299       return (curr->ptr == data) ? TRUE : FALSE;
    300   }
    301   return FALSE;
    302 }
    303 
    304 /* returns TRUE if the given handle is head of the recv pipe */
    305 bool Curl_recvpipe_head(struct Curl_easy *data,
    306                         struct connectdata *conn)
    307 {
    308   return pipe_head(data, &conn->recv_pipe);
    309 }
    310 
    311 /* returns TRUE if the given handle is head of the send pipe */
    312 bool Curl_sendpipe_head(struct Curl_easy *data,
    313                         struct connectdata *conn)
    314 {
    315   return pipe_head(data, &conn->send_pipe);
    316 }
    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 
    326 bool Curl_pipeline_checkget_write(struct Curl_easy *data,
    327                                   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 
    349 bool Curl_pipeline_checkget_read(struct Curl_easy *data,
    350                                  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  */
    367 void 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  */
    375 void Curl_pipeline_leave_read(struct connectdata *conn)
    376 {
    377   conn->readchannel_inuse = FALSE;
    378 }
    379 
    380 
    381 #if 0
    382 void print_pipeline(struct connectdata *conn)
    383 {
    384   struct curl_llist_element *curr;
    385   struct connectbundle *cb_ptr;
    386   struct Curl_easy *data = conn->data;
    387 
    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
    405