Base code merged to SPIN 2.4
[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-2014, 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: (%"
89           CURL_FORMAT_CURL_OFF_T "/%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 result;
103
104   pipeline = conn->send_pipe;
105
106   result = Curl_addHandleToPipeline(handle, pipeline);
107
108   if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) {
109     /* this is a new one as head, expire it */
110     conn->writechannel_inuse = FALSE; /* not in use yet */
111     Curl_expire(conn->send_pipe->head->ptr, 1);
112   }
113
114 #if 0 /* enable for pipeline debugging */
115   print_pipeline(conn);
116 #endif
117
118   return result;
119 }
120
121 /* Move this transfer from the sending list to the receiving list.
122
123    Pay special attention to the new sending list "leader" as it needs to get
124    checked to update what sockets it acts on.
125
126 */
127 void Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle *handle,
128                                              struct connectdata *conn)
129 {
130   struct curl_llist_element *curr;
131
132   curr = conn->send_pipe->head;
133   while(curr) {
134     if(curr->ptr == handle) {
135       Curl_llist_move(conn->send_pipe, curr,
136                       conn->recv_pipe, conn->recv_pipe->tail);
137
138       if(conn->send_pipe->head) {
139         /* Since there's a new easy handle at the start of the send pipeline,
140            set its timeout value to 1ms to make it trigger instantly */
141         conn->writechannel_inuse = FALSE; /* not used now */
142 #ifdef DEBUGBUILD
143         infof(conn->data, "%p is at send pipe head B!\n",
144               (void *)conn->send_pipe->head->ptr);
145 #endif
146         Curl_expire(conn->send_pipe->head->ptr, 1);
147       }
148
149       /* The receiver's list is not really interesting here since either this
150          handle is now first in the list and we'll deal with it soon, or
151          another handle is already first and thus is already taken care of */
152
153       break; /* we're done! */
154     }
155     curr = curr->next;
156   }
157 }
158
159 bool Curl_pipeline_site_blacklisted(struct SessionHandle *handle,
160                                     struct connectdata *conn)
161 {
162   if(handle->multi) {
163     struct curl_llist *blacklist =
164       Curl_multi_pipelining_site_bl(handle->multi);
165
166     if(blacklist) {
167       struct curl_llist_element *curr;
168
169       curr = blacklist->head;
170       while(curr) {
171         struct site_blacklist_entry *site;
172
173         site = curr->ptr;
174         if(Curl_raw_equal(site->hostname, conn->host.name) &&
175            site->port == conn->remote_port) {
176           infof(handle, "Site %s:%d is pipeline blacklisted\n",
177                 conn->host.name, conn->remote_port);
178           return TRUE;
179         }
180         curr = curr->next;
181       }
182     }
183   }
184   return FALSE;
185 }
186
187 CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
188                                            struct curl_llist **list_ptr)
189 {
190   struct curl_llist *old_list = *list_ptr;
191   struct curl_llist *new_list = NULL;
192
193   if(sites) {
194     new_list = Curl_llist_alloc((curl_llist_dtor) site_blacklist_llist_dtor);
195     if(!new_list)
196       return CURLM_OUT_OF_MEMORY;
197
198     /* Parse the URLs and populate the list */
199     while(*sites) {
200       char *hostname;
201       char *port;
202       struct site_blacklist_entry *entry;
203
204       hostname = strdup(*sites);
205       if(!hostname) {
206         Curl_llist_destroy(new_list, NULL);
207         return CURLM_OUT_OF_MEMORY;
208       }
209
210       entry = malloc(sizeof(struct site_blacklist_entry));
211       if(!entry) {
212         free(hostname);
213         Curl_llist_destroy(new_list, NULL);
214         return CURLM_OUT_OF_MEMORY;
215       }
216
217       port = strchr(hostname, ':');
218       if(port) {
219         *port = '\0';
220         port++;
221         entry->port = (unsigned short)strtol(port, NULL, 10);
222       }
223       else {
224         /* Default port number for HTTP */
225         entry->port = 80;
226       }
227
228       entry->hostname = hostname;
229
230       if(!Curl_llist_insert_next(new_list, new_list->tail, entry)) {
231         site_blacklist_llist_dtor(NULL, entry);
232         Curl_llist_destroy(new_list, NULL);
233         return CURLM_OUT_OF_MEMORY;
234       }
235
236       sites++;
237     }
238   }
239
240   /* Free the old list */
241   if(old_list) {
242     Curl_llist_destroy(old_list, NULL);
243   }
244
245   /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
246   *list_ptr = new_list;
247
248   return CURLM_OK;
249 }
250
251 bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle,
252                                       char *server_name)
253 {
254   if(handle->multi && server_name) {
255     struct curl_llist *blacklist =
256       Curl_multi_pipelining_server_bl(handle->multi);
257
258     if(blacklist) {
259       struct curl_llist_element *curr;
260
261       curr = blacklist->head;
262       while(curr) {
263         char *bl_server_name;
264
265         bl_server_name = curr->ptr;
266         if(Curl_raw_nequal(bl_server_name, server_name,
267                            strlen(bl_server_name))) {
268           infof(handle, "Server %s is blacklisted\n", server_name);
269           return TRUE;
270         }
271         curr = curr->next;
272       }
273     }
274
275     DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name));
276   }
277   return FALSE;
278 }
279
280 CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
281                                              struct curl_llist **list_ptr)
282 {
283   struct curl_llist *old_list = *list_ptr;
284   struct curl_llist *new_list = NULL;
285
286   if(servers) {
287     new_list = Curl_llist_alloc((curl_llist_dtor) server_blacklist_llist_dtor);
288     if(!new_list)
289       return CURLM_OUT_OF_MEMORY;
290
291     /* Parse the URLs and populate the list */
292     while(*servers) {
293       char *server_name;
294
295       server_name = strdup(*servers);
296       if(!server_name)
297         return CURLM_OUT_OF_MEMORY;
298
299       if(!Curl_llist_insert_next(new_list, new_list->tail, server_name))
300         return CURLM_OUT_OF_MEMORY;
301
302       servers++;
303     }
304   }
305
306   /* Free the old list */
307   if(old_list) {
308     Curl_llist_destroy(old_list, NULL);
309   }
310
311   /* This might be NULL if sites == NULL, i.e the blacklist is cleared */
312   *list_ptr = new_list;
313
314   return CURLM_OK;
315 }
316
317 #if 0
318 void print_pipeline(struct connectdata *conn)
319 {
320   struct curl_llist_element *curr;
321   struct connectbundle *cb_ptr;
322   struct SessionHandle *data = conn->data;
323
324   cb_ptr = conn->bundle;
325
326   if(cb_ptr) {
327     curr = cb_ptr->conn_list->head;
328     while(curr) {
329       conn = curr->ptr;
330       infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n",
331             conn->connection_id,
332             (void *)conn,
333             conn->send_pipe->size,
334             conn->recv_pipe->size);
335       curr = curr->next;
336     }
337   }
338 }
339
340 #endif