curl: refactor curlsink, rename to curlhttpsink

Split into base, tls and http classes.

https://bugzilla.gnome.org/show_bug.cgi?id=653741
This commit is contained in:
Patricia Muscalu 2012-01-23 09:00:47 +01:00 committed by Tim-Philipp Müller
parent e9cba00c6a
commit ae39cbfa57
13 changed files with 1609 additions and 633 deletions

View file

@ -310,7 +310,7 @@ GST_PLUGINS_NONPORTED=" aiff \
sdi siren speed subenc stereo tta videofilters \
videomeasure videosignal vmnc \
decklink fbdev linsys vcd \
apexsink cdaudio cog curl dc1394 dirac directfb resindvd \
apexsink cdaudio cog dc1394 dirac directfb resindvd \
gsettings jp2k ladspa mimic \
musepack musicbrainz nas neon ofa openal opencv rsvg sdl sndfile soundtouch spandsp spc timidity \
directsound direct3d directdraw direct3d9 acm wininet \

View file

@ -61,11 +61,17 @@ IGNORE_CFILES =
EXAMPLE_CFILES = \
$(top_srcdir)/ext/directfb/dfb-example.c
# $(top_srcdir)/ext/curl/gstcurlfilesink.h
# $(top_srcdir)/ext/curl/gstcurlftpsink.h
# $(top_srcdir)/ext/curl/gstcurlsmtpsink.h
EXTRA_HFILES = \
$(top_srcdir)/ext/assrender/gstassrender.h \
$(top_srcdir)/ext/celt/gstceltdec.h \
$(top_srcdir)/ext/celt/gstceltenc.h \
$(top_srcdir)/ext/curl/gstcurlsink.h \
$(top_srcdir)/ext/curl/gstcurlbasesink.h \
$(top_srcdir)/ext/curl/gstcurlhttpsink.h \
$(top_srcdir)/ext/curl/gstcurltlssink.h \
$(top_srcdir)/ext/dc1394/gstdc1394.h \
$(top_srcdir)/ext/directfb/dfbvideosink.h \
$(top_srcdir)/ext/dts/gstdtsdec.h \

View file

@ -1,16 +1,22 @@
plugin_LTLIBRARIES = libgstcurl.la
libgstcurl_la_SOURCES = gstcurl.c gstcurlsink.c
libgstcurl_la_SOURCES = gstcurl.c \
gstcurlbasesink.c \
gstcurltlssink.c \
gstcurlhttpsink.c
libgstcurl_la_CFLAGS = \
$(GST_PLUGINS_BAD_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(CURL_CFLAGS)
libgstcurl_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) \
$(GST_BASE_LIBS) \
$(GST_LIBS) \
$(CURL_LIBS)
libgstcurl_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstcurl_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = gstcurlsink.h
noinst_HEADERS = gstcurlbasesink.h \
gstcurltlssink.h \
gstcurlhttpsink.h

View file

@ -20,14 +20,16 @@
#include <config.h>
#endif
#include "gstcurlsink.h"
#include "gstcurlbasesink.h"
#include "gstcurltlssink.h"
#include "gstcurlhttpsink.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_element_register (plugin, "curlsink", GST_RANK_NONE,
GST_TYPE_CURL_SINK))
if (!gst_element_register (plugin, "curlhttpsink", GST_RANK_NONE,
GST_TYPE_CURL_HTTP_SINK))
return FALSE;
return TRUE;

File diff suppressed because it is too large Load diff

116
ext/curl/gstcurlbasesink.h Normal file
View file

@ -0,0 +1,116 @@
/* GStreamer
* Copyright (C) 2011 Axis Communications <dev-gstreamer@axis.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_CURL_BASE_SINK__
#define __GST_CURL_BASE_SINK__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
#include <curl/curl.h>
G_BEGIN_DECLS
#define GST_TYPE_CURL_BASE_SINK \
(gst_curl_base_sink_get_type())
#define GST_CURL_BASE_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_CURL_BASE_SINK, GstCurlBaseSink))
#define GST_CURL_BASE_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CURL_BASE_SINK, GstCurlBaseSinkClass))
#define GST_CURL_BASE_SINK_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CURL_BASE_SINK, GstCurlBaseSinkClass))
#define GST_IS_CURL_BASE_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_CURL_BASE_SINK))
#define GST_IS_CURL_BASE_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CURL_BASE_SINK))
typedef struct _GstCurlBaseSink GstCurlBaseSink;
typedef struct _GstCurlBaseSinkClass GstCurlBaseSinkClass;
typedef struct _TransferBuffer TransferBuffer;
typedef struct _TransferCondition TransferCondition;
struct _TransferBuffer
{
guint8 *ptr;
size_t len;
size_t offset;
};
struct _TransferCondition
{
GCond *cond;
gboolean data_sent;
gboolean data_available;
gboolean wait_for_response;
};
struct _GstCurlBaseSink
{
GstBaseSink parent;
/*< private > */
CURLM *multi_handle;
CURL *curl;
GstPollFD fd;
GstPoll *fdset;
GThread *transfer_thread;
GstFlowReturn flow_ret;
TransferBuffer *transfer_buf;
TransferCondition *transfer_cond;
gint num_buffers_per_packet;
gint timeout;
gchar *url;
gchar *user;
gchar *passwd;
gchar *file_name;
guint qos_dscp;
gboolean transfer_thread_close;
gboolean new_file;
gboolean is_live;
};
struct _GstCurlBaseSinkClass
{
GstBaseSinkClass parent_class;
/* vmethods */
gboolean (*set_protocol_dynamic_options_unlocked) (GstCurlBaseSink * sink);
gboolean (*set_options_unlocked) (GstCurlBaseSink * sink);
void (*set_mime_type) (GstCurlBaseSink * sink, GstCaps * caps);
void (*transfer_prepare_poll_wait) (GstCurlBaseSink * sink);
glong (*transfer_get_response_code) (GstCurlBaseSink * sink, glong resp);
gboolean (*transfer_verify_response_code) (GstCurlBaseSink * sink);
GstFlowReturn (*prepare_transfer) (GstCurlBaseSink * sink);
void (*handle_transfer) (GstCurlBaseSink * sink);
size_t (*transfer_read_cb) (void *curl_ptr, size_t size, size_t nmemb,
void *stream);
size_t (*transfer_data_buffer) (GstCurlBaseSink * sink, void *curl_ptr,
size_t block_size, guint * last_chunk);
size_t (*flush_data_unlocked) (GstCurlBaseSink * sink, void *curl_ptr,
size_t block_size, gboolean new_file);
};
GType gst_curl_base_sink_get_type (void);
void gst_curl_base_sink_transfer_thread_notify_unlocked
(GstCurlBaseSink * sink);
void gst_curl_base_sink_transfer_thread_close (GstCurlBaseSink * sink);
void gst_curl_base_sink_set_live (GstCurlBaseSink * sink, gboolean live);
gboolean gst_curl_base_sink_is_live (GstCurlBaseSink * sink);
G_END_DECLS
#endif

476
ext/curl/gstcurlhttpsink.c Normal file
View file

@ -0,0 +1,476 @@
/* GStreamer
* Copyright (C) 2011 Axis Communications <dev-gstreamer@axis.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-curlhttpsink
* @short_description: sink that uploads data to a server using libcurl
* @see_also:
*
* This is a network sink that uses libcurl as a client to upload data to
* an HTTP server.
*
* <refsect2>
* <title>Example launch line (upload a JPEG file to an HTTP server)</title>
* |[
* gst-launch filesrc location=image.jpg ! jpegparse ! curlhttpsink \
* file-name=image.jpg \
* location=http://192.168.0.1:8080/cgi-bin/patupload.cgi/ \
* user=test passwd=test \
* content-type=image/jpeg \
* use-content-length=false
* ]|
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <curl/curl.h>
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "gstcurltlssink.h"
#include "gstcurlhttpsink.h"
/* Default values */
#define GST_CAT_DEFAULT gst_curl_http_sink_debug
#define DEFAULT_TIMEOUT 30
#define DEFAULT_PROXY_PORT 3128
#define DEFAULT_USE_CONTENT_LENGTH FALSE
#define RESPONSE_CONNECT_PROXY 200
/* Plugin specific settings */
GST_DEBUG_CATEGORY_STATIC (gst_curl_http_sink_debug);
enum
{
PROP_0,
PROP_PROXY,
PROP_PROXY_PORT,
PROP_PROXY_USER_NAME,
PROP_PROXY_USER_PASSWD,
PROP_USE_CONTENT_LENGTH,
PROP_CONTENT_TYPE
};
/* Object class function declarations */
static void gst_curl_http_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_curl_http_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_curl_http_sink_finalize (GObject * gobject);
static gboolean gst_curl_http_sink_set_header_unlocked
(GstCurlBaseSink * bcsink);
static gboolean gst_curl_http_sink_set_options_unlocked
(GstCurlBaseSink * bcsink);
static void gst_curl_http_sink_set_mime_type
(GstCurlBaseSink * bcsink, GstCaps * caps);
static gboolean gst_curl_http_sink_transfer_verify_response_code
(GstCurlBaseSink * bcsink);
static void gst_curl_http_sink_transfer_prepare_poll_wait
(GstCurlBaseSink * bcsink);
#define gst_curl_http_sink_parent_class parent_class
G_DEFINE_TYPE (GstCurlHttpSink, gst_curl_http_sink, GST_TYPE_CURL_TLS_SINK);
/* private functions */
static gboolean proxy_setup (GstCurlBaseSink * bcsink);
static void
gst_curl_http_sink_class_init (GstCurlHttpSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstCurlBaseSinkClass *gstcurlbasesink_class = (GstCurlBaseSinkClass *) klass;
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (gst_curl_http_sink_debug, "curlhttpsink", 0,
"curl http sink element");
GST_DEBUG_OBJECT (klass, "class_init");
gst_element_class_set_details_simple (element_class,
"Curl http sink",
"Sink/Network",
"Upload data over HTTP/HTTPS protocol using libcurl",
"Patricia Muscalu <patricia@axis.com>");
gstcurlbasesink_class->set_protocol_dynamic_options_unlocked =
gst_curl_http_sink_set_header_unlocked;
gstcurlbasesink_class->set_options_unlocked =
gst_curl_http_sink_set_options_unlocked;
gstcurlbasesink_class->set_mime_type = gst_curl_http_sink_set_mime_type;
gstcurlbasesink_class->transfer_verify_response_code =
gst_curl_http_sink_transfer_verify_response_code;
gstcurlbasesink_class->transfer_prepare_poll_wait =
gst_curl_http_sink_transfer_prepare_poll_wait;
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_curl_http_sink_finalize);
gobject_class->set_property = gst_curl_http_sink_set_property;
gobject_class->get_property = gst_curl_http_sink_get_property;
g_object_class_install_property (gobject_class, PROP_PROXY,
g_param_spec_string ("proxy", "Proxy", "HTTP proxy server URI", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PROXY_PORT,
g_param_spec_int ("proxy-port", "Proxy port",
"HTTP proxy server port", 0, G_MAXINT, DEFAULT_PROXY_PORT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PROXY_USER_NAME,
g_param_spec_string ("proxy-user", "Proxy user name",
"Proxy user name to use for proxy authentication",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PROXY_USER_PASSWD,
g_param_spec_string ("proxy-passwd", "Proxy user password",
"Proxy user password to use for proxy authentication",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_USE_CONTENT_LENGTH,
g_param_spec_boolean ("use-content-length", "Use content length header",
"Use the Content-Length HTTP header instead of "
"Transfer-Encoding header", DEFAULT_USE_CONTENT_LENGTH,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_CONTENT_TYPE,
g_param_spec_string ("content-type", "Content type",
"The mime type of the body of the request", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
gst_curl_http_sink_init (GstCurlHttpSink * sink)
{
sink->header_list = NULL;
sink->use_content_length = DEFAULT_USE_CONTENT_LENGTH;
sink->content_type = NULL;
sink->proxy_port = DEFAULT_PROXY_PORT;
sink->proxy_headers_set = FALSE;
sink->proxy_auth = FALSE;
sink->use_proxy = FALSE;
sink->proxy_conn_established = FALSE;
sink->proxy_resp = -1;
}
static void
gst_curl_http_sink_finalize (GObject * gobject)
{
GstCurlHttpSink *this = GST_CURL_HTTP_SINK (gobject);
GST_DEBUG ("finalizing curlhttpsink");
g_free (this->proxy);
g_free (this->proxy_user);
g_free (this->proxy_passwd);
g_free (this->content_type);
if (this->header_list) {
curl_slist_free_all (this->header_list);
this->header_list = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (gobject);
}
static void
gst_curl_http_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstCurlHttpSink *sink;
GstState cur_state;
g_return_if_fail (GST_IS_CURL_HTTP_SINK (object));
sink = GST_CURL_HTTP_SINK (object);
gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0);
if (cur_state != GST_STATE_PLAYING && cur_state != GST_STATE_PAUSED) {
GST_OBJECT_LOCK (sink);
switch (prop_id) {
case PROP_PROXY:
g_free (sink->proxy);
sink->proxy = g_value_dup_string (value);
GST_DEBUG_OBJECT (sink, "proxy set to %s", sink->proxy);
break;
case PROP_PROXY_PORT:
sink->proxy_port = g_value_get_int (value);
GST_DEBUG_OBJECT (sink, "proxy port set to %d", sink->proxy_port);
break;
case PROP_PROXY_USER_NAME:
g_free (sink->proxy_user);
sink->proxy_user = g_value_dup_string (value);
GST_DEBUG_OBJECT (sink, "proxy user set to %s", sink->proxy_user);
break;
case PROP_PROXY_USER_PASSWD:
g_free (sink->proxy_passwd);
sink->proxy_passwd = g_value_dup_string (value);
GST_DEBUG_OBJECT (sink, "proxy password set to %s", sink->proxy_passwd);
break;
case PROP_USE_CONTENT_LENGTH:
sink->use_content_length = g_value_get_boolean (value);
GST_DEBUG_OBJECT (sink, "use_content_length set to %d",
sink->use_content_length);
break;
case PROP_CONTENT_TYPE:
g_free (sink->content_type);
sink->content_type = g_value_dup_string (value);
GST_DEBUG_OBJECT (sink, "content type set to %s", sink->content_type);
break;
default:
GST_DEBUG_OBJECT (sink, "invalid property id %d", prop_id);
break;
}
GST_OBJECT_UNLOCK (sink);
return;
}
/* in PLAYING or PAUSED state */
GST_OBJECT_LOCK (sink);
switch (prop_id) {
case PROP_CONTENT_TYPE:
g_free (sink->content_type);
sink->content_type = g_value_dup_string (value);
GST_DEBUG_OBJECT (sink, "content type set to %s", sink->content_type);
break;
default:
GST_WARNING_OBJECT (sink, "cannot set property when PLAYING");
break;
}
GST_OBJECT_UNLOCK (sink);
}
static void
gst_curl_http_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstCurlHttpSink *sink;
g_return_if_fail (GST_IS_CURL_HTTP_SINK (object));
sink = GST_CURL_HTTP_SINK (object);
switch (prop_id) {
case PROP_PROXY:
g_value_set_string (value, sink->proxy);
break;
case PROP_PROXY_PORT:
g_value_set_int (value, sink->proxy_port);
break;
case PROP_PROXY_USER_NAME:
g_value_set_string (value, sink->proxy_user);
break;
case PROP_PROXY_USER_PASSWD:
g_value_set_string (value, sink->proxy_passwd);
break;
case PROP_USE_CONTENT_LENGTH:
g_value_set_boolean (value, sink->use_content_length);
break;
case PROP_CONTENT_TYPE:
g_value_set_string (value, sink->content_type);
break;
default:
GST_DEBUG_OBJECT (sink, "invalid property id");
break;
}
}
static gboolean
gst_curl_http_sink_set_header_unlocked (GstCurlBaseSink * bcsink)
{
GstCurlHttpSink *sink = GST_CURL_HTTP_SINK (bcsink);
gchar *tmp;
if (sink->header_list) {
curl_slist_free_all (sink->header_list);
sink->header_list = NULL;
}
if (!sink->proxy_headers_set && sink->use_proxy) {
sink->header_list = curl_slist_append (sink->header_list,
"Content-Length: 0");
sink->proxy_headers_set = TRUE;
goto set_headers;
}
if (sink->use_content_length) {
/* if content length is used we assume that every buffer is one
* entire file, which is the case when uploading several jpegs */
tmp =
g_strdup_printf ("Content-Length: %d", (int) bcsink->transfer_buf->len);
sink->header_list = curl_slist_append (sink->header_list, tmp);
g_free (tmp);
} else {
/* when sending a POST request to a HTTP 1.1 server, you can send data
* without knowing the size before starting the POST if you use chunked
* encoding */
sink->header_list = curl_slist_append (sink->header_list,
"Transfer-Encoding: chunked");
}
tmp = g_strdup_printf ("Content-Type: %s", sink->content_type);
sink->header_list = curl_slist_append (sink->header_list, tmp);
g_free (tmp);
set_headers:
tmp = g_strdup_printf ("Content-Disposition: attachment; filename="
"\"%s\"", bcsink->file_name);
sink->header_list = curl_slist_append (sink->header_list, tmp);
g_free (tmp);
curl_easy_setopt (bcsink->curl, CURLOPT_HTTPHEADER, sink->header_list);
return TRUE;
}
static gboolean
gst_curl_http_sink_set_options_unlocked (GstCurlBaseSink * bcsink)
{
GstCurlHttpSink *sink = GST_CURL_HTTP_SINK (bcsink);
GstCurlTlsSinkClass *parent_class;
/* proxy settings */
if (sink->proxy != NULL && strlen (sink->proxy)) {
if (!proxy_setup (bcsink)) {
return FALSE;
}
}
curl_easy_setopt (bcsink->curl, CURLOPT_POST, 1L);
/* FIXME: check user & passwd */
curl_easy_setopt (bcsink->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
parent_class = GST_CURL_TLS_SINK_GET_CLASS (sink);
if (g_str_has_prefix (bcsink->url, "https://")) {
return parent_class->set_options_unlocked (bcsink);
}
return TRUE;
}
static gboolean
gst_curl_http_sink_transfer_verify_response_code (GstCurlBaseSink * bcsink)
{
GstCurlHttpSink *sink = GST_CURL_HTTP_SINK (bcsink);
glong resp;
curl_easy_getinfo (bcsink->curl, CURLINFO_RESPONSE_CODE, &resp);
GST_DEBUG_OBJECT (sink, "response code: %ld", resp);
if (resp < 100 || resp >= 300) {
GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
("HTTP response error: (received: %ld)", resp), (NULL));
return FALSE;
}
return TRUE;
}
static void
gst_curl_http_sink_transfer_prepare_poll_wait (GstCurlBaseSink * bcsink)
{
GstCurlHttpSink *sink = GST_CURL_HTTP_SINK (bcsink);
if (!sink->proxy_conn_established
&& (sink->proxy_resp != RESPONSE_CONNECT_PROXY)
&& sink->proxy_auth) {
curl_easy_getinfo (bcsink->curl, CURLINFO_HTTP_CONNECTCODE,
&sink->proxy_resp);
if ((sink->proxy_resp == RESPONSE_CONNECT_PROXY)) {
GST_LOG ("received HTTP/1.0 200 Connection Established");
/* Workaround: redefine HTTP headers before connecting to HTTP server.
* When talking to proxy, the Content-Length: 0 is send with the request.
*/
curl_multi_remove_handle (bcsink->multi_handle, bcsink->curl);
gst_curl_http_sink_set_header_unlocked (bcsink);
curl_multi_add_handle (bcsink->multi_handle, bcsink->curl);
sink->proxy_conn_established = TRUE;
}
}
}
// FIXME check this: why critical when no mime is set???
static void
gst_curl_http_sink_set_mime_type (GstCurlBaseSink * bcsink, GstCaps * caps)
{
GstCurlHttpSink *sink = GST_CURL_HTTP_SINK (bcsink);
GstStructure *structure;
const gchar *mime_type;
if (sink->content_type != NULL) {
return;
}
structure = gst_caps_get_structure (caps, 0);
mime_type = gst_structure_get_name (structure);
sink->content_type = g_strdup (mime_type);
}
static gboolean
proxy_setup (GstCurlBaseSink * bcsink)
{
GstCurlHttpSink *sink = GST_CURL_HTTP_SINK (bcsink);
if (curl_easy_setopt (bcsink->curl, CURLOPT_PROXY, sink->proxy)
!= CURLE_OK) {
return FALSE;
}
if (curl_easy_setopt (bcsink->curl, CURLOPT_PROXYPORT, sink->proxy_port)
!= CURLE_OK) {
return FALSE;
}
if (sink->proxy_user != NULL &&
strlen (sink->proxy_user) &&
sink->proxy_passwd != NULL && strlen (sink->proxy_passwd)) {
curl_easy_setopt (bcsink->curl, CURLOPT_PROXYUSERNAME, sink->proxy_user);
curl_easy_setopt (bcsink->curl, CURLOPT_PROXYPASSWORD, sink->proxy_passwd);
curl_easy_setopt (bcsink->curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
sink->proxy_auth = TRUE;
}
if (g_str_has_prefix (bcsink->url, "https://")) {
/* tunnel all operations through a given HTTP proxy */
if (curl_easy_setopt (bcsink->curl, CURLOPT_HTTPPROXYTUNNEL, 1L)
!= CURLE_OK) {
return FALSE;
}
}
sink->use_proxy = TRUE;
return TRUE;
}

View file

@ -0,0 +1,69 @@
/* GStreamer
* Copyright (C) 2011 Axis Communications <dev-gstreamer@axis.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_CURL_HTTP_SINK__
#define __GST_CURL_HTTP_SINK__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
#include <curl/curl.h>
#include "gstcurltlssink.h"
G_BEGIN_DECLS
#define GST_TYPE_CURL_HTTP_SINK \
(gst_curl_http_sink_get_type())
#define GST_CURL_HTTP_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_CURL_HTTP_SINK, GstCurlHttpSink))
#define GST_CURL_HTTP_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CURL_HTTP_SINK, GstCurlHttpSinkClass))
#define GST_IS_CURL_HTTP_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_CURL_HTTP_SINK))
#define GST_IS_CURL_HTTP_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CURL_HTTP_SINK))
typedef struct _GstCurlHttpSink GstCurlHttpSink;
typedef struct _GstCurlHttpSinkClass GstCurlHttpSinkClass;
struct _GstCurlHttpSink
{
GstCurlTlsSink parent;
/*< private > */
struct curl_slist *header_list;
gchar *proxy;
guint proxy_port;
gchar *proxy_user;
gchar *proxy_passwd;
gboolean use_content_length;
gchar *content_type;
gboolean use_proxy;
gboolean proxy_headers_set;
gboolean proxy_auth;
gboolean proxy_conn_established;
glong proxy_resp;
};
struct _GstCurlHttpSinkClass
{
GstCurlTlsSinkClass parent_class;
};
GType gst_curl_http_sink_get_type (void);
G_END_DECLS
#endif

View file

@ -1,100 +0,0 @@
/* GStreamer
* Copyright (C) 2011 Axis Communications <dev-gstreamer@axis.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_CURL_SINK__
#define __GST_CURL_SINK__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
#include <curl/curl.h>
G_BEGIN_DECLS
#define GST_TYPE_CURL_SINK \
(gst_curl_sink_get_type())
#define GST_CURL_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_CURL_SINK, GstCurlSink))
#define GST_CURL_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CURL_SINK, GstCurlSinkClass))
#define GST_IS_CURL_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_CURL_SINK))
#define GST_IS_CURL_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CURL_SINK))
typedef struct _GstCurlSink GstCurlSink;
typedef struct _GstCurlSinkClass GstCurlSinkClass;
typedef struct _TransferBuffer TransferBuffer;
typedef struct _TransferCondition TransferCondition;
struct _TransferBuffer {
guint8 *ptr;
size_t len;
size_t offset;
};
struct _TransferCondition {
GCond *cond;
gboolean data_sent;
gboolean data_available;
};
struct _GstCurlSink
{
GstBaseSink parent;
/*< private >*/
CURLM *multi_handle;
CURL *curl;
struct curl_slist *header_list;
GstPollFD fd;
GstPoll *fdset;
GThread *transfer_thread;
GstFlowReturn flow_ret;
TransferBuffer *transfer_buf;
TransferCondition *transfer_cond;
gint num_buffers_per_packet;
gint timeout;
gchar *url;
gchar *user;
gchar *passwd;
gchar *proxy;
guint proxy_port;
gchar *proxy_user;
gchar *proxy_passwd;
gchar *file_name;
guint qos_dscp;
gboolean accept_self_signed;
gboolean use_content_length;
gboolean transfer_thread_close;
gboolean new_file;
gchar *content_type;
gboolean proxy_headers_set;
};
struct _GstCurlSinkClass
{
GstBaseSinkClass parent_class;
};
GType gst_curl_sink_get_type (void);
G_END_DECLS
#endif

279
ext/curl/gstcurltlssink.c Normal file
View file

@ -0,0 +1,279 @@
/* GStreamer
* Copyright (C) 2011 Axis Communications <dev-gstreamer@axis.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-curltlssink
* @short_description: sink that uploads data to a server using libcurl
* @see_also:
*
* This is a network sink that uses libcurl.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <curl/curl.h>
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "gstcurlbasesink.h"
#include "gstcurltlssink.h"
/* Default values */
#define GST_CAT_DEFAULT gst_curl_tls_sink_debug
#define DEFAULT_INSECURE TRUE
/* Plugin specific settings */
GST_DEBUG_CATEGORY_STATIC (gst_curl_tls_sink_debug);
enum
{
PROP_0,
PROP_CA_CERT,
PROP_CA_PATH,
PROP_CRYPTO_ENGINE,
PROP_INSECURE
};
/* Object class function declarations */
static void gst_curl_tls_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_curl_tls_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_curl_tls_sink_finalize (GObject * gobject);
static gboolean gst_curl_tls_sink_set_options_unlocked
(GstCurlBaseSink * bcsink);
#define gst_curl_tls_sink_parent_class parent_class
G_DEFINE_TYPE (GstCurlTlsSink, gst_curl_tls_sink, GST_TYPE_CURL_BASE_SINK);
/* private functions */
static void
gst_curl_tls_sink_class_init (GstCurlTlsSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (gst_curl_tls_sink_debug, "curltlssink", 0,
"curl tls sink element");
GST_DEBUG_OBJECT (klass, "class_init");
gst_element_class_set_details_simple (element_class,
"Curl tls sink",
"Sink/Network",
"Upload data over TLS protocol using libcurl",
"Patricia Muscalu <patricia@axis.com>");
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_curl_tls_sink_finalize);
gobject_class->set_property = gst_curl_tls_sink_set_property;
gobject_class->get_property = gst_curl_tls_sink_get_property;
klass->set_options_unlocked = gst_curl_tls_sink_set_options_unlocked;
g_object_class_install_property (gobject_class, PROP_CA_CERT,
g_param_spec_string ("ca-cert",
"CA certificate",
"CA certificate to use in order to verify the peer",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_CA_PATH,
g_param_spec_string ("ca-path",
"CA path",
"CA directory path to use in order to verify the peer",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_CRYPTO_ENGINE,
g_param_spec_string ("crypto-engine",
"OpenSSL crypto engine",
"OpenSSL crytpo engine to use for cipher operations",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_INSECURE,
g_param_spec_boolean ("insecure",
"Perform insecure SSL connections",
"Allow curl to perform insecure SSL connections",
DEFAULT_INSECURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
gst_curl_tls_sink_init (GstCurlTlsSink * sink)
{
sink->ca_cert = NULL;
sink->ca_path = NULL;
sink->crypto_engine = NULL;
sink->insecure = DEFAULT_INSECURE;
}
static void
gst_curl_tls_sink_finalize (GObject * gobject)
{
GstCurlTlsSink *this = GST_CURL_TLS_SINK (gobject);
GST_DEBUG ("finalizing curltlssink");
g_free (this->ca_cert);
g_free (this->ca_path);
g_free (this->crypto_engine);
G_OBJECT_CLASS (parent_class)->finalize (gobject);
}
static void
gst_curl_tls_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstCurlTlsSink *sink;
GstState cur_state;
g_return_if_fail (GST_IS_CURL_TLS_SINK (object));
sink = GST_CURL_TLS_SINK (object);
gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0);
if (cur_state != GST_STATE_PLAYING && cur_state != GST_STATE_PAUSED) {
GST_OBJECT_LOCK (sink);
switch (prop_id) {
case PROP_CA_CERT:
g_free (sink->ca_cert);
sink->ca_cert = g_value_dup_string (value);
sink->insecure = FALSE;
GST_DEBUG_OBJECT (sink, "ca_cert set to %s", sink->ca_cert);
break;
case PROP_CA_PATH:
g_free (sink->ca_path);
sink->ca_path = g_value_dup_string (value);
sink->insecure = FALSE;
GST_DEBUG_OBJECT (sink, "ca_path set to %s", sink->ca_path);
break;
case PROP_CRYPTO_ENGINE:
g_free (sink->crypto_engine);
sink->crypto_engine = g_value_dup_string (value);
GST_DEBUG_OBJECT (sink, "crypto_engine set to %s", sink->crypto_engine);
break;
case PROP_INSECURE:
sink->insecure = g_value_get_boolean (value);
GST_DEBUG_OBJECT (sink, "insecure set to %d", sink->insecure);
break;
}
GST_OBJECT_UNLOCK (sink);
return;
}
GST_OBJECT_UNLOCK (sink);
}
static void
gst_curl_tls_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstCurlTlsSink *sink;
g_return_if_fail (GST_IS_CURL_TLS_SINK (object));
sink = GST_CURL_TLS_SINK (object);
switch (prop_id) {
case PROP_CA_CERT:
g_value_set_string (value, sink->ca_cert);
break;
case PROP_CA_PATH:
g_value_set_string (value, sink->ca_path);
break;
case PROP_CRYPTO_ENGINE:
g_value_set_string (value, sink->crypto_engine);
break;
case PROP_INSECURE:
g_value_set_boolean (value, sink->insecure);
break;
default:
GST_DEBUG_OBJECT (sink, "invalid property id");
break;
}
}
static gboolean
gst_curl_tls_sink_set_options_unlocked (GstCurlBaseSink * bcsink)
{
GstCurlTlsSink *sink = GST_CURL_TLS_SINK (bcsink);
if (!g_str_has_prefix (bcsink->url, "http")) {
curl_easy_setopt (bcsink->curl, CURLOPT_USE_SSL, 1L);
}
/* crypto engine */
if ((g_strcmp0 (sink->crypto_engine, "auto") == 0) ||
(sink->crypto_engine == NULL)) {
if (curl_easy_setopt (bcsink->curl, CURLOPT_SSLENGINE_DEFAULT, 1L)
!= CURLE_OK) {
GST_WARNING ("Error setting up default SSL engine.");
}
} else {
if (curl_easy_setopt (bcsink->curl, CURLOPT_SSLENGINE,
sink->crypto_engine) == CURLE_SSL_ENGINE_NOTFOUND) {
GST_WARNING ("Error setting up SSL engine: %s.", sink->crypto_engine);
}
}
/* note that, using ca-path can allow libcurl to make SSL-connections much more
* efficiently than using ca-cert if the ca-cert ile
* contains many CA certificates. */
if (sink->ca_cert != NULL && strlen (sink->ca_cert)) {
GST_DEBUG ("setting ca cert");
curl_easy_setopt (bcsink->curl, CURLOPT_CAINFO, sink->ca_cert);
}
if (sink->ca_path != NULL && strlen (sink->ca_path)) {
GST_DEBUG ("setting ca path");
curl_easy_setopt (bcsink->curl, CURLOPT_CAPATH, sink->ca_path);
}
if (!sink->insecure) {
/* identify authenticity of the peer's certificate */
curl_easy_setopt (bcsink->curl, CURLOPT_SSL_VERIFYPEER, 1L);
/* when CURLOPT_SSL_VERIFYHOST is 2, the commonName or subjectAltName
* fields are verified */
curl_easy_setopt (bcsink->curl, CURLOPT_SSL_VERIFYHOST, 2L);
return TRUE;
}
/* allow "insecure" SSL connections and transfers */
if (sink->insecure) {
curl_easy_setopt (bcsink->curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt (bcsink->curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
return TRUE;
}

66
ext/curl/gstcurltlssink.h Normal file
View file

@ -0,0 +1,66 @@
/* GStreamer
* Copyright (C) 2011 Axis Communications <dev-gstreamer@axis.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_CURL_TLS_SINK__
#define __GST_CURL_TLS_SINK__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
#include <curl/curl.h>
#include "gstcurlbasesink.h"
G_BEGIN_DECLS
#define GST_TYPE_CURL_TLS_SINK \
(gst_curl_tls_sink_get_type())
#define GST_CURL_TLS_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_CURL_TLS_SINK, GstCurlTlsSink))
#define GST_CURL_TLS_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CURL_TLS_SINK, GstCurlTlsSinkClass))
#define GST_CURL_TLS_SINK_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CURL_TLS_SINK, GstCurlTlsSinkClass))
#define GST_IS_CURL_TLS_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_CURL_TLS_SINK))
#define GST_IS_CURL_TLS_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CURL_TLS_SINK))
typedef struct _GstCurlTlsSink GstCurlTlsSink;
typedef struct _GstCurlTlsSinkClass GstCurlTlsSinkClass;
struct _GstCurlTlsSink
{
GstCurlBaseSink parent;
/*< private > */
gchar *ca_cert;
gchar *ca_path;
gchar *crypto_engine;
gboolean insecure;
};
struct _GstCurlTlsSinkClass
{
GstCurlBaseSinkClass parent_class;
/* vmethods */
gboolean (*set_options_unlocked) (GstCurlBaseSink * sink);
};
GType gst_curl_tls_sink_get_type (void);
G_END_DECLS
#endif

View file

@ -147,6 +147,12 @@ else
check_opus =
endif
if USE_CURL
check_curl = elements/curlhttpsink
else
check_curl =
endif
VALGRIND_TO_FIX = \
elements/mpeg2enc \
elements/mplex \
@ -177,6 +183,7 @@ check_PROGRAMS = \
$(check_timidity) \
$(check_kate) \
$(check_opus) \
$(check_curl) \
elements/autoconvert \
elements/autovideoconvert \
elements/asfmux \

View file

@ -0,0 +1,136 @@
/*
* Unittest for curlhttpsink
*/
#include <gst/check/gstcheck.h>
#include <glib/gstdio.h>
#include <curl/curl.h>
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstPad *srcpad;
static GstElement *sink;
static GstElement *
setup_curlhttpsink (void)
{
GST_DEBUG ("setup_curlhttpsink");
sink = gst_check_setup_element ("curlhttpsink");
srcpad = gst_check_setup_src_pad (sink, &srctemplate);
gst_pad_set_active (srcpad, TRUE);
return sink;
}
static void
cleanup_curlhttpsink (GstElement * sink)
{
GST_DEBUG ("cleanup_curlhttpsink");
gst_check_teardown_src_pad (sink);
gst_check_teardown_element (sink);
}
GST_START_TEST (test_properties)
{
GstElement *sink;
gchar *res_location = NULL;
gchar *res_file_name = NULL;
gchar *res_user;
gchar *res_passwd;
gchar *res_proxy;
guint res_proxy_port;
gchar *res_proxy_user;
gchar *res_proxy_passwd;
gchar *res_content_type;
gboolean res_use_content_length;
sink = setup_curlhttpsink ();
g_object_set (G_OBJECT (sink),
"location", "mylocation",
"file-name","myfile",
"user", "user",
"passwd", "passwd",
"proxy", "myproxy",
"proxy-port", 7777,
"proxy-user", "proxy_user",
"proxy-passwd", "proxy_passwd",
"content-type", "image/jpeg",
"use-content-length", TRUE,
NULL);
g_object_get (sink,
"location", &res_location,
"file-name", &res_file_name,
"user", &res_user,
"passwd", &res_passwd,
"proxy", &res_proxy,
"proxy-port", &res_proxy_port,
"proxy-user", &res_proxy_user,
"proxy-passwd", &res_proxy_passwd,
"content-type", &res_content_type,
"use-content-length", &res_use_content_length,
NULL);
fail_unless (strncmp (res_location, "mylocation", strlen ("mylocation"))
== 0);
fail_unless (strncmp (res_file_name, "myfile", strlen ("myfile"))
== 0);
fail_unless (strncmp (res_user, "user", strlen ("user")) == 0);
fail_unless (strncmp (res_passwd, "passwd", strlen ("passwd")) == 0);
fail_unless (strncmp (res_proxy, "myproxy", strlen ("myproxy")) == 0);
fail_unless (res_proxy_port == 7777);
fail_unless (strncmp (res_proxy_user, "proxy_user", strlen ("proxy_user"))
== 0);
fail_unless (strncmp (res_proxy_passwd, "proxy_passwd",
strlen ("proxy_passwd")) == 0);
fail_unless (strncmp (res_content_type, "image/jpeg", strlen ("image/jpeg"))
== 0);
fail_unless (res_use_content_length == TRUE);
g_free (res_location);
g_free (res_file_name);
g_free (res_user);
g_free (res_passwd);
g_free (res_proxy);
g_free (res_proxy_user);
g_free (res_proxy_passwd);
g_free (res_content_type);
/* new properties */
g_object_set (G_OBJECT (sink), "location", "newlocation", NULL);
g_object_get (sink, "location", &res_location, NULL);
fail_unless (strncmp (res_location, "newlocation", strlen ("newlocation"))
== 0);
g_free (res_location);
g_object_set (G_OBJECT (sink), "file-name", "newfile", NULL);
g_object_get (sink, "file-name", &res_file_name, NULL);
fail_unless (strncmp (res_file_name, "newfile", strlen ("newfile"))
== 0);
g_free (res_file_name);
cleanup_curlhttpsink (sink);
}
GST_END_TEST;
static Suite *
curlsink_suite (void)
{
Suite *s = suite_create ("curlhttpsink");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_set_timeout (tc_chain, 20);
tcase_add_test (tc_chain, test_properties);
return s;
}
GST_CHECK_MAIN (curlsink);