1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2013 - 2017, 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 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
site_blacklist_llist_dtor(void * user,void * element)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
server_blacklist_llist_dtor(void * user,void * element)53 static void server_blacklist_llist_dtor(void *user, void *element)
54 {
55 (void)user;
56 free(element);
57 }
58
Curl_pipeline_penalized(struct Curl_easy * data,struct connectdata * conn)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
addHandleToPipeline(struct Curl_easy * data,struct curl_llist * pipeline)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
Curl_add_handle_to_pipeline(struct Curl_easy * handle,struct connectdata * conn)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 */
Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy * handle,struct connectdata * conn)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
Curl_pipeline_site_blacklisted(struct Curl_easy * handle,struct connectdata * conn)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
Curl_pipeline_set_site_blacklist(char ** sites,struct curl_llist * list)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
Curl_pipeline_server_blacklisted(struct Curl_easy * handle,char * server_name)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
Curl_pipeline_set_server_blacklist(char ** servers,struct curl_llist * list)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
pipe_head(struct Curl_easy * data,struct curl_llist * pipeline)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 */
Curl_recvpipe_head(struct Curl_easy * data,struct connectdata * conn)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 */
Curl_sendpipe_head(struct Curl_easy * data,struct connectdata * conn)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
Curl_pipeline_checkget_write(struct Curl_easy * data,struct connectdata * conn)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
Curl_pipeline_checkget_read(struct Curl_easy * data,struct connectdata * conn)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 */
Curl_pipeline_leave_write(struct connectdata * conn)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 */
Curl_pipeline_leave_read(struct connectdata * conn)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