1 /*
2  * GstCurlHttpSrc
3  * Copyright 2017 British Broadcasting Corporation - Research and Development
4  *
5  * Author: Sam Hurst <samuelh@rd.bbc.co.uk>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  *
25  * Alternatively, the contents of this file may be used under the
26  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
27  * which case the following provisions apply instead of the ones
28  * mentioned above:
29  *
30  * This library is free software; you can redistribute it and/or
31  * modify it under the terms of the GNU Library General Public
32  * License as published by the Free Software Foundation; either
33  * version 2 of the License, or (at your option) any later version.
34  *
35  * This library is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
38  * Library General Public License for more details.
39  *
40  * You should have received a copy of the GNU Library General Public
41  * License along with this library; if not, write to the
42  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
43  * Boston, MA 02111-1307, USA.
44  */
45 
46 #include "gstcurlqueue.h"
47 
48 /**
49  * gst_curl_http_src_add_queue_item:
50  *
51  * Function to add an item to a queue. If the queue is empty (i.e. NULL), then
52  * it creates the new head of the queue, otherwise it scans to the end and adds
53  * the entry there.
54  * @param queue The queue to add an item to. Can be NULL.
55  * @param s The item to be added to the queue.
56  * @return Returns TRUE (0) on success, FALSE (!0) is an error.
57  */
58 gboolean
gst_curl_http_src_add_queue_item(GstCurlHttpSrcQueueElement ** queue,GstCurlHttpSrc * s)59 gst_curl_http_src_add_queue_item (GstCurlHttpSrcQueueElement ** queue,
60     GstCurlHttpSrc * s)
61 {
62   GstCurlHttpSrcQueueElement *insert_point;
63 
64   if (*queue == NULL) {
65     /* Queue is currently empty, so create a new item on the head */
66     *queue = (GstCurlHttpSrcQueueElement *)
67         g_malloc (sizeof (GstCurlHttpSrcQueueElement));
68     if (*queue == NULL) {
69       return FALSE;
70     }
71     insert_point = *queue;
72   } else {
73     insert_point = *queue;
74     while (insert_point->next != NULL) {
75       insert_point = insert_point->next;
76     }
77     insert_point->next = (GstCurlHttpSrcQueueElement *)
78         g_malloc (sizeof (GstCurlHttpSrcQueueElement));
79     if (insert_point->next == NULL) {
80       return FALSE;
81     }
82     insert_point = insert_point->next;
83   }
84 
85   insert_point->p = s;
86   g_atomic_int_set (&insert_point->running, 0);
87   insert_point->next = NULL;
88   s->connection_status = GSTCURL_CONNECTED;
89   return TRUE;
90 }
91 
92 /**
93  * gst_curl_http_src_remove_queue_item:
94  *
95  * Function to remove an item from a queue.
96  * @param queue The queue to remove an item from.
97  * @param s The item to be removed.
98  * @return Returns TRUE if item removed, FALSE if item couldn't be found.
99  */
100 gboolean
gst_curl_http_src_remove_queue_item(GstCurlHttpSrcQueueElement ** queue,GstCurlHttpSrc * s)101 gst_curl_http_src_remove_queue_item (GstCurlHttpSrcQueueElement ** queue,
102     GstCurlHttpSrc * s)
103 {
104   GstCurlHttpSrcQueueElement *prev_qelement, *this_qelement;
105 
106   prev_qelement = NULL;
107   this_qelement = *queue;
108   while (this_qelement && (this_qelement->p != s)) {
109     prev_qelement = this_qelement;
110     this_qelement = this_qelement->next;
111   }
112   if (this_qelement == NULL) {
113     /* Reached end of list without finding anything */
114     return FALSE;
115   }
116 
117   /* First queue item matched. */
118   if (prev_qelement == NULL) {
119     /* First and only element? If so, free the element and make queue NULL */
120     if (this_qelement->next == NULL) {
121       g_free (*queue);
122       *queue = NULL;
123       return TRUE;
124     } else {
125       *queue = this_qelement->next;
126     }
127   } else {
128     prev_qelement->next = this_qelement->next;
129   }
130   g_free (this_qelement);
131   s->connection_status = GSTCURL_NOT_CONNECTED;
132   return TRUE;
133 }
134 
135 /**
136  * gst_curl_http_src_remove_queue_handle:
137  *
138  * Convenience function to remove an item from a queue by it's contained curl
139  * handle. Only ever called from within the multi loop when the CURL handle
140  * returns, so it's safe to assume that the transfer completed and the result
141  * can be set as GSTCURL_RETURN_DONE (which doesn't necessarily mean that the
142  * transfer was a success, just that CURL is finished with it)
143  * @param queue The queue to remove an item from.
144  * @param s The item to be removed.
145  * @return Returns TRUE if item removed, FALSE if item couldn't be found.
146  */
147 gboolean
gst_curl_http_src_remove_queue_handle(GstCurlHttpSrcQueueElement ** queue,CURL * handle,CURLcode result)148 gst_curl_http_src_remove_queue_handle (GstCurlHttpSrcQueueElement ** queue,
149     CURL * handle, CURLcode result)
150 {
151   GstCurlHttpSrcQueueElement *prev_qelement, *this_qelement;
152 
153   prev_qelement = NULL;
154   this_qelement = *queue;
155   while (this_qelement && (this_qelement->p->curl_handle != handle)) {
156     prev_qelement = this_qelement;
157     this_qelement = this_qelement->next;
158   }
159   if (this_qelement == NULL) {
160     /* Reached end of list without finding anything */
161     return FALSE;
162   }
163 
164   /*GST_DEBUG_OBJECT (this_qelement->p,
165      "Removing queue item via curl handle for URI %s",
166      this_qelement->p->uri); */
167   /* First, signal the transfer owner thread to wake up */
168   g_mutex_lock (&this_qelement->p->buffer_mutex);
169   g_cond_signal (&this_qelement->p->buffer_cond);
170   if (this_qelement->p->state != GSTCURL_UNLOCK) {
171     this_qelement->p->state = GSTCURL_DONE;
172   } else {
173     this_qelement->p->pending_state = GSTCURL_DONE;
174   }
175   this_qelement->p->connection_status = GSTCURL_NOT_CONNECTED;
176   this_qelement->p->curl_result = result;
177   g_mutex_unlock (&this_qelement->p->buffer_mutex);
178 
179   /* First queue item matched. */
180   if (prev_qelement == NULL) {
181     /* First and only element? If so, free the element and make queue NULL */
182     if (this_qelement->next == NULL) {
183       g_free (*queue);
184       *queue = NULL;
185       return TRUE;
186     } else {
187       *queue = this_qelement->next;
188     }
189   } else {
190     prev_qelement->next = this_qelement->next;
191   }
192   g_free (this_qelement);
193   return TRUE;
194 }
195