1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "curl_setup.h"
24
25#include <curl/curl.h>
26
27#include "urldata.h"
28#include "url.h"
29#include "progress.h"
30#include "multiif.h"
31#include "pipeline.h"
32#include "sendf.h"
33#include "rawstr.h"
34#include "bundles.h"
35
36#include "curl_memory.h"
37/* The last #include file should be: */
38#include "memdebug.h"
39
40struct site_blacklist_entry {
41  char *hostname;
42  unsigned short port;
43};
44
45static 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  Curl_safefree(entry);
52}
53
54static void server_blacklist_llist_dtor(void *user, void *element)
55{
56  char *server_name = element;
57  (void)user;
58
59  Curl_safefree(server_name);
60}
61
62bool Curl_pipeline_penalized(struct SessionHandle *data,
63                             struct connectdata *conn)
64{
65  if(data) {
66    bool penalized = FALSE;
67    curl_off_t penalty_size =
68      Curl_multi_content_length_penalty_size(data->multi);
69    curl_off_t chunk_penalty_size =
70      Curl_multi_chunk_length_penalty_size(data->multi);
71    curl_off_t recv_size = -2; /* Make it easy to spot in the log */
72
73    /* Find the head of the recv pipe, if any */
74    if(conn->recv_pipe && conn->recv_pipe->head) {
75      struct SessionHandle *recv_handle = conn->recv_pipe->head->ptr;
76
77      recv_size = recv_handle->req.size;
78
79      if(penalty_size > 0 && recv_size > penalty_size)
80        penalized = TRUE;
81    }
82
83    if(chunk_penalty_size > 0 &&
84       (curl_off_t)conn->chunk.datasize > chunk_penalty_size)
85      penalized = TRUE;
86
87    infof(data, "Conn: %d (%p) Receive pipe weight: (%d/%d), penalized: %d\n",
88          conn->connection_id, conn, recv_size,
89          conn->chunk.datasize, penalized);
90    return penalized;
91  }
92  return FALSE;
93}
94
95CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle,
96                                     struct connectdata *conn)
97{
98  struct curl_llist_element *sendhead = conn->send_pipe->head;
99  struct curl_llist *pipeline;
100  CURLcode rc;
101
102  pipeline = conn->send_pipe;
103
104  infof(conn->data, "Adding handle: conn: %p\n", conn);
105  infof(conn->data, "Adding handle: send: %d\n", conn->send_pipe->size);
106  infof(conn->data, "Adding handle: recv: %d\n", conn->recv_pipe->size);
107  rc = Curl_addHandleToPipeline(handle, pipeline);
108
109  if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) {
110    /* this is a new one as head, expire it */
111    conn->writechannel_inuse = FALSE; /* not in use yet */
112#ifdef DEBUGBUILD
113    infof(conn->data, "%p is at send pipe head!\n",
114          conn->send_pipe->head->ptr);
115#endif
116    Curl_expire(conn->send_pipe->head->ptr, 1);
117  }
118
119  print_pipeline(conn);
120
121  return rc;
122}
123
124/* Move this transfer from the sending list to the receiving list.
125
126   Pay special attention to the new sending list "leader" as it needs to get
127   checked to update what sockets it acts on.
128
129*/
130void Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle *handle,
131                                             struct connectdata *conn)
132{
133  struct curl_llist_element *curr;
134
135  curr = conn->send_pipe->head;
136  while(curr) {
137    if(curr->ptr == handle) {
138      Curl_llist_move(conn->send_pipe, curr,
139                      conn->recv_pipe, conn->recv_pipe->tail);
140
141      if(conn->send_pipe->head) {
142        /* Since there's a new easy handle at the start of the send pipeline,
143           set its timeout value to 1ms to make it trigger instantly */
144        conn->writechannel_inuse = FALSE; /* not used now */
145#ifdef DEBUGBUILD
146        infof(conn->data, "%p is at send pipe head B!\n",
147              conn->send_pipe->head->ptr);
148#endif
149        Curl_expire(conn->send_pipe->head->ptr, 1);
150      }
151
152      /* The receiver's list is not really interesting here since either this
153         handle is now first in the list and we'll deal with it soon, or
154         another handle is already first and thus is already taken care of */
155
156      break; /* we're done! */
157    }
158    curr = curr->next;
159  }
160}
161
162bool Curl_pipeline_site_blacklisted(struct SessionHandle *handle,
163                                    struct connectdata *conn)
164{
165  if(handle->multi) {
166    struct curl_llist *blacklist =
167      Curl_multi_pipelining_site_bl(handle->multi);
168
169    if(blacklist) {
170      struct curl_llist_element *curr;
171
172      curr = blacklist->head;
173      while(curr) {
174        struct site_blacklist_entry *site;
175
176        site = curr->ptr;
177        if(Curl_raw_equal(site->hostname, conn->host.name) &&
178           site->port == conn->remote_port) {
179          infof(handle, "Site %s:%d is pipeline blacklisted\n",
180                conn->host.name, conn->remote_port);
181          return TRUE;
182        }
183        curr = curr->next;
184      }
185    }
186  }
187  return FALSE;
188}
189
190CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
191                                           struct curl_llist **list_ptr)
192{
193  struct curl_llist *old_list = *list_ptr;
194  struct curl_llist *new_list = NULL;
195
196  if(sites) {
197    new_list = Curl_llist_alloc((curl_llist_dtor) site_blacklist_llist_dtor);
198    if(!new_list)
199      return CURLM_OUT_OF_MEMORY;
200
201    /* Parse the URLs and populate the list */
202    while(*sites) {
203      char *hostname;
204      char *port;
205      struct site_blacklist_entry *entry;
206
207      entry = malloc(sizeof(struct site_blacklist_entry));
208
209      hostname = strdup(*sites);
210      if(!hostname)
211        return CURLM_OUT_OF_MEMORY;
212
213      port = strchr(hostname, ':');
214      if(port) {
215        *port = '\0';
216        port++;
217        entry->port = (unsigned short)strtol(port, NULL, 10);
218      }
219      else {
220        /* Default port number for HTTP */
221        entry->port = 80;
222      }
223
224      entry->hostname = hostname;
225
226      if(!Curl_llist_insert_next(new_list, new_list->tail, entry))
227        return CURLM_OUT_OF_MEMORY;
228
229      sites++;
230    }
231  }
232
233  /* Free the old list */
234  if(old_list) {
235    Curl_llist_destroy(old_list, NULL);
236  }
237
238  /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
239  *list_ptr = new_list;
240
241  return CURLM_OK;
242}
243
244bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle,
245                                      char *server_name)
246{
247  if(handle->multi) {
248    struct curl_llist *blacklist =
249      Curl_multi_pipelining_server_bl(handle->multi);
250
251    if(blacklist) {
252      struct curl_llist_element *curr;
253
254      curr = blacklist->head;
255      while(curr) {
256        char *bl_server_name;
257
258        bl_server_name = curr->ptr;
259        if(Curl_raw_nequal(bl_server_name, server_name,
260                           strlen(bl_server_name))) {
261          infof(handle, "Server %s is blacklisted\n", server_name);
262          return TRUE;
263        }
264        curr = curr->next;
265      }
266    }
267
268    infof(handle, "Server %s is not blacklisted\n", server_name);
269  }
270  return FALSE;
271}
272
273CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
274                                             struct curl_llist **list_ptr)
275{
276  struct curl_llist *old_list = *list_ptr;
277  struct curl_llist *new_list = NULL;
278
279  if(servers) {
280    new_list = Curl_llist_alloc((curl_llist_dtor) server_blacklist_llist_dtor);
281    if(!new_list)
282      return CURLM_OUT_OF_MEMORY;
283
284    /* Parse the URLs and populate the list */
285    while(*servers) {
286      char *server_name;
287
288      server_name = strdup(*servers);
289      if(!server_name)
290        return CURLM_OUT_OF_MEMORY;
291
292      if(!Curl_llist_insert_next(new_list, new_list->tail, server_name))
293        return CURLM_OUT_OF_MEMORY;
294
295      servers++;
296    }
297  }
298
299  /* Free the old list */
300  if(old_list) {
301    Curl_llist_destroy(old_list, NULL);
302  }
303
304  /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
305  *list_ptr = new_list;
306
307  return CURLM_OK;
308}
309
310
311void print_pipeline(struct connectdata *conn)
312{
313  struct curl_llist_element *curr;
314  struct connectbundle *cb_ptr;
315  struct SessionHandle *data = conn->data;
316
317  cb_ptr = conn->bundle;
318
319  if(cb_ptr) {
320    curr = cb_ptr->conn_list->head;
321    while(curr) {
322      conn = curr->ptr;
323      infof(data, "- Conn %d (%p) send_pipe: %d, recv_pipe: %d\n",
324            conn->connection_id,
325            conn,
326            conn->send_pipe->size,
327            conn->recv_pipe->size);
328      curr = curr->next;
329    }
330  }
331}
332