418058afa0dedbdbc6f0dafd6779819da9d53624
[platform/upstream/curl.git] / lib / pipeline.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
9  * Copyright (C) 2013, 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 #include "bundles.h"
36
37 #include "curl_memory.h"
38 /* The last #include file should be: */
39 #include "memdebug.h"
40
41 struct site_blacklist_entry {
42   char *hostname;
43   unsigned short port;
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
51   Curl_safefree(entry->hostname);
52   Curl_safefree(entry);
53 }
54
55 static void server_blacklist_llist_dtor(void *user, void *element)
56 {
57   char *server_name = element;
58   (void)user;
59
60   Curl_safefree(server_name);
61 }
62
63 bool Curl_pipeline_penalized(struct SessionHandle *data,
64                              struct connectdata *conn)
65 {
66   if(data) {
67     bool penalized = FALSE;
68     curl_off_t penalty_size =
69       Curl_multi_content_length_penalty_size(data->multi);
70     curl_off_t chunk_penalty_size =
71       Curl_multi_chunk_length_penalty_size(data->multi);
72     curl_off_t recv_size = -2; /* Make it easy to spot in the log */
73
74     /* Find the head of the recv pipe, if any */
75     if(conn->recv_pipe && conn->recv_pipe->head) {
76       struct SessionHandle *recv_handle = conn->recv_pipe->head->ptr;
77
78       recv_size = recv_handle->req.size;
79
80       if(penalty_size > 0 && recv_size > penalty_size)
81         penalized = TRUE;
82     }
83
84     if(chunk_penalty_size > 0 &&
85        (curl_off_t)conn->chunk.datasize > chunk_penalty_size)
86       penalized = TRUE;
87
88     infof(data, "Conn: %ld (%p) Receive pipe weight: (%" FORMAT_OFF_T
89           "/%zu), penalized: %s\n",
90           conn->connection_id, (void *)conn, recv_size,
91           conn->chunk.datasize, penalized?"TRUE":"FALSE");
92     return penalized;
93   }
94   return FALSE;
95 }
96
97 CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle,
98                                      struct connectdata *conn)
99 {
100   struct curl_llist_element *sendhead = conn->send_pipe->head;
101   struct curl_llist *pipeline;
102   CURLcode rc;
103
104   pipeline = conn->send_pipe;
105
106   infof(conn->data, "Adding handle: conn: %p\n", (void *)conn);
107   infof(conn->data, "Adding handle: send: %d\n", conn->send_pipe->size);
108   infof(conn->data, "Adding handle: recv: %d\n", conn->recv_pipe->size);
109   rc = Curl_addHandleToPipeline(handle, pipeline);
110
111   if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) {
112     /* this is a new one as head, expire it */
113     conn->writechannel_inuse = FALSE; /* not in use yet */
114 #ifdef DEBUGBUILD
115     infof(conn->data, "%p is at send pipe head!\n",
116           (void *)conn->send_pipe->head->ptr);
117 #endif
118     Curl_expire(conn->send_pipe->head->ptr, 1);
119   }
120
121   print_pipeline(conn);
122
123   return rc;
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 SessionHandle *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         conn->writechannel_inuse = FALSE; /* 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, 1);
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 SessionHandle *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(Curl_raw_equal(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_ptr)
194 {
195   struct curl_llist *old_list = *list_ptr;
196   struct curl_llist *new_list = NULL;
197
198   if(sites) {
199     new_list = Curl_llist_alloc((curl_llist_dtor) site_blacklist_llist_dtor);
200     if(!new_list)
201       return CURLM_OUT_OF_MEMORY;
202
203     /* Parse the URLs and populate the list */
204     while(*sites) {
205       char *hostname;
206       char *port;
207       struct site_blacklist_entry *entry;
208
209       entry = malloc(sizeof(struct site_blacklist_entry));
210
211       hostname = strdup(*sites);
212       if(!hostname)
213         return CURLM_OUT_OF_MEMORY;
214
215       port = strchr(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       entry->hostname = hostname;
227
228       if(!Curl_llist_insert_next(new_list, new_list->tail, entry))
229         return CURLM_OUT_OF_MEMORY;
230
231       sites++;
232     }
233   }
234
235   /* Free the old list */
236   if(old_list) {
237     Curl_llist_destroy(old_list, NULL);
238   }
239
240   /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
241   *list_ptr = new_list;
242
243   return CURLM_OK;
244 }
245
246 bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle,
247                                       char *server_name)
248 {
249   if(handle->multi) {
250     struct curl_llist *blacklist =
251       Curl_multi_pipelining_server_bl(handle->multi);
252
253     if(blacklist) {
254       struct curl_llist_element *curr;
255
256       curr = blacklist->head;
257       while(curr) {
258         char *bl_server_name;
259
260         bl_server_name = curr->ptr;
261         if(Curl_raw_nequal(bl_server_name, server_name,
262                            strlen(bl_server_name))) {
263           infof(handle, "Server %s is blacklisted\n", server_name);
264           return TRUE;
265         }
266         curr = curr->next;
267       }
268     }
269
270     infof(handle, "Server %s is not blacklisted\n", server_name);
271   }
272   return FALSE;
273 }
274
275 CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
276                                              struct curl_llist **list_ptr)
277 {
278   struct curl_llist *old_list = *list_ptr;
279   struct curl_llist *new_list = NULL;
280
281   if(servers) {
282     new_list = Curl_llist_alloc((curl_llist_dtor) server_blacklist_llist_dtor);
283     if(!new_list)
284       return CURLM_OUT_OF_MEMORY;
285
286     /* Parse the URLs and populate the list */
287     while(*servers) {
288       char *server_name;
289
290       server_name = strdup(*servers);
291       if(!server_name)
292         return CURLM_OUT_OF_MEMORY;
293
294       if(!Curl_llist_insert_next(new_list, new_list->tail, server_name))
295         return CURLM_OUT_OF_MEMORY;
296
297       servers++;
298     }
299   }
300
301   /* Free the old list */
302   if(old_list) {
303     Curl_llist_destroy(old_list, NULL);
304   }
305
306   /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
307   *list_ptr = new_list;
308
309   return CURLM_OK;
310 }
311
312
313 void print_pipeline(struct connectdata *conn)
314 {
315   struct curl_llist_element *curr;
316   struct connectbundle *cb_ptr;
317   struct SessionHandle *data = conn->data;
318
319   cb_ptr = conn->bundle;
320
321   if(cb_ptr) {
322     curr = cb_ptr->conn_list->head;
323     while(curr) {
324       conn = curr->ptr;
325       infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n",
326             conn->connection_id,
327             (void *)conn,
328             conn->send_pipe->size,
329             conn->recv_pipe->size);
330       curr = curr->next;
331     }
332   }
333 }