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 - 2017, 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 "/%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 
     93 static CURLcode addHandleToPipeline(struct Curl_easy *data,
     94                                     struct curl_llist *pipeline)
     95 {
     96   Curl_llist_insert_next(pipeline, pipeline->tail, data,
     97                          &data->pipeline_queue);
     98   return CURLE_OK;
     99 }
    100 
    101 
    102 CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle,
    103                                      struct connectdata *conn)
    104 {
    105   struct curl_llist_element *sendhead = conn->send_pipe.head;
    106   struct curl_llist *pipeline;
    107   CURLcode result;
    108 
    109   pipeline = &conn->send_pipe;
    110 
    111   result = addHandleToPipeline(handle, pipeline);
    112 
    113   if(pipeline == &conn->send_pipe && sendhead != conn->send_pipe.head) {
    114     /* this is a new one as head, expire it */
    115     Curl_pipeline_leave_write(conn); /* not in use yet */
    116     Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
    117   }
    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 */
    132 void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *handle,
    133                                              struct connectdata *conn)
    134 {
    135   struct curl_llist_element *curr;
    136 
    137   curr = conn->send_pipe.head;
    138   while(curr) {
    139     if(curr->ptr == handle) {
    140       Curl_llist_move(&conn->send_pipe, curr,
    141                       &conn->recv_pipe, conn->recv_pipe.tail);
    142 
    143       if(conn->send_pipe.head) {
    144         /* 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",
    149               (void *)conn->send_pipe.head->ptr);
    150 #endif
    151         Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
    152       }
    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 
    164 bool Curl_pipeline_site_blacklisted(struct Curl_easy *handle,
    165                                     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;
    179         if(strcasecompare(site->hostname, conn->host.name) &&
    180            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 
    192 CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
    193                                            struct curl_llist *list)
    194 {
    195   /* Free the old list */
    196   if(list->size)
    197     Curl_llist_destroy(list, NULL);
    198 
    199   if(sites) {
    200     Curl_llist_init(list, (curl_llist_dtor) site_blacklist_llist_dtor);
    201 
    202     /* Parse the URLs and populate the list */
    203     while(*sites) {
    204       char *port;
    205       struct site_blacklist_entry *entry;
    206 
    207       entry = malloc(sizeof(struct site_blacklist_entry) + strlen(*sites));
    208       if(!entry) {
    209         Curl_llist_destroy(list, NULL);
    210         return CURLM_OUT_OF_MEMORY;
    211       }
    212       strcpy(entry->hostname, *sites);
    213 
    214       port = strchr(entry->hostname, ':');
    215       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 
    225       Curl_llist_insert_next(list, list->tail, entry, &entry->list);
    226       sites++;
    227     }
    228   }
    229 
    230   return CURLM_OK;
    231 }
    232 
    233 struct blacklist_node {
    234   struct curl_llist_element list;
    235   char server_name[1];
    236 };
    237 
    238 bool Curl_pipeline_server_blacklisted(struct Curl_easy *handle,
    239                                       char *server_name)
    240 {
    241   if(handle->multi && server_name) {
    242     struct curl_llist *list =
    243       Curl_multi_pipelining_server_bl(handle->multi);
    244 
    245     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;
    252       }
    253       e = e->next;
    254     }
    255 
    256     DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name));
    257   }
    258   return FALSE;
    259 }
    260 
    261 CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
    262                                              struct curl_llist *list)
    263 {
    264   /* Free the old list */
    265   if(list->size)
    266     Curl_llist_destroy(list, NULL);
    267 
    268   if(servers) {
    269     Curl_llist_init(list, (curl_llist_dtor) server_blacklist_llist_dtor);
    270 
    271     /* Parse the URLs and populate the list */
    272     while(*servers) {
    273       struct blacklist_node *n;
    274       size_t len = strlen(*servers);
    275 
    276       n = malloc(sizeof(struct blacklist_node) + len);
    277       if(!n) {
    278         Curl_llist_destroy(list, NULL);
    279         return CURLM_OUT_OF_MEMORY;
    280       }
    281       strcpy(n->server_name, *servers);
    282 
    283       Curl_llist_insert_next(list, list->tail, n, &n->list);
    284       servers++;
    285     }
    286   }
    287 
    288 
    289   return CURLM_OK;
    290 }
    291 
    292 static bool pipe_head(struct Curl_easy *data,
    293                       struct curl_llist *pipeline)
    294 {
    295   if(pipeline) {
    296     struct curl_llist_element *curr = pipeline->head;
    297     if(curr)
    298       return (curr->ptr == data) ? TRUE : FALSE;
    299   }
    300   return FALSE;
    301 }
    302 
    303 /* returns TRUE if the given handle is head of the recv pipe */
    304 bool Curl_recvpipe_head(struct Curl_easy *data,
    305                         struct connectdata *conn)
    306 {
    307   return pipe_head(data, &conn->recv_pipe);
    308 }
    309 
    310 /* returns TRUE if the given handle is head of the send pipe */
    311 bool Curl_sendpipe_head(struct Curl_easy *data,
    312                         struct connectdata *conn)
    313 {
    314   return pipe_head(data, &conn->send_pipe);
    315 }
    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 
    325 bool Curl_pipeline_checkget_write(struct Curl_easy *data,
    326                                   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 
    348 bool Curl_pipeline_checkget_read(struct Curl_easy *data,
    349                                  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  */
    366 void 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  */
    374 void Curl_pipeline_leave_read(struct connectdata *conn)
    375 {
    376   conn->readchannel_inuse = FALSE;
    377 }
    378 
    379 
    380 #if 0
    381 void print_pipeline(struct connectdata *conn)
    382 {
    383   struct curl_llist_element *curr;
    384   struct connectbundle *cb_ptr;
    385   struct Curl_easy *data = conn->data;
    386 
    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
    404