diff mbox

[v2] curl: Allow a cookie or cookies to be sent with http/https requests.

Message ID 1409324592-31888-2-git-send-email-rjones@redhat.com
State New
Headers show

Commit Message

Richard W.M. Jones Aug. 29, 2014, 3:03 p.m. UTC
In order to access VMware ESX efficiently, we need to send a session
cookie.  This patch is very simple and just allows you to send that
session cookie.  It punts on the question of how you get the session
cookie in the first place, but in practice you can just run a `curl'
command against the server and extract the cookie that way.

To use it, add file.cookie to the curl URL.  For example:

$ qemu-img info 'json: {
    "file.driver":"https",
    "file.url":"https://vcenter/folder/Windows%202003/Windows%202003-flat.vmdk?dcPath=Datacenter&dsName=datastore1",
    "file.sslverify":"off",
    "file.cookie":"vmware_soap_session=\"52a01262-bf93-ccce-d379-8dabb3e55560\""}'
image: [...]
file format: raw
virtual size: 8.0G (8589934592 bytes)
disk size: unavailable

Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
---
 block/curl.c    | 16 ++++++++++++++++
 qemu-options.hx |  5 +++++
 2 files changed, 21 insertions(+)

Comments

Matthew Booth Aug. 29, 2014, 3:06 p.m. UTC | #1
Looks good. Please add:

Reviewed-by: Matthew Booth <mbooth@redhat.com>

Matt

On 29/08/14 16:03, Richard W.M. Jones wrote:
> In order to access VMware ESX efficiently, we need to send a session
> cookie.  This patch is very simple and just allows you to send that
> session cookie.  It punts on the question of how you get the session
> cookie in the first place, but in practice you can just run a `curl'
> command against the server and extract the cookie that way.
> 
> To use it, add file.cookie to the curl URL.  For example:
> 
> $ qemu-img info 'json: {
>     "file.driver":"https",
>     "file.url":"https://vcenter/folder/Windows%202003/Windows%202003-flat.vmdk?dcPath=Datacenter&dsName=datastore1",
>     "file.sslverify":"off",
>     "file.cookie":"vmware_soap_session=\"52a01262-bf93-ccce-d379-8dabb3e55560\""}'
> image: [...]
> file format: raw
> virtual size: 8.0G (8589934592 bytes)
> disk size: unavailable
> 
> Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
> ---
>  block/curl.c    | 16 ++++++++++++++++
>  qemu-options.hx |  5 +++++
>  2 files changed, 21 insertions(+)
> 
> diff --git a/block/curl.c b/block/curl.c
> index f59615d..fa1a258 100644
> --- a/block/curl.c
> +++ b/block/curl.c
> @@ -71,6 +71,7 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
>  #define CURL_BLOCK_OPT_URL       "url"
>  #define CURL_BLOCK_OPT_READAHEAD "readahead"
>  #define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
> +#define CURL_BLOCK_OPT_COOKIE    "cookie"
>  
>  struct BDRVCURLState;
>  
> @@ -109,6 +110,7 @@ typedef struct BDRVCURLState {
>      char *url;
>      size_t readahead_size;
>      bool sslverify;
> +    char *cookie;
>      bool accept_range;
>      AioContext *aio_context;
>  } BDRVCURLState;
> @@ -382,6 +384,9 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
>          curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
>          curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
>                           (long) s->sslverify);
> +        if (s->cookie) {
> +            curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
> +        }
>          curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
>          curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
>                           (void *)curl_read_cb);
> @@ -489,6 +494,11 @@ static QemuOptsList runtime_opts = {
>              .type = QEMU_OPT_BOOL,
>              .help = "Verify SSL certificate"
>          },
> +        {
> +            .name = CURL_BLOCK_OPT_COOKIE,
> +            .type = QEMU_OPT_STRING,
> +            .help = "Pass the cookie or list of cookies with each request"
> +        },
>          { /* end of list */ }
>      },
>  };
> @@ -501,6 +511,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
>      QemuOpts *opts;
>      Error *local_err = NULL;
>      const char *file;
> +    const char *cookie;
>      double d;
>  
>      static int inited = 0;
> @@ -527,6 +538,9 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
>  
>      s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
>  
> +    cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
> +    s->cookie = g_strdup(cookie);
> +
>      file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
>      if (file == NULL) {
>          error_setg(errp, "curl block driver requires an 'url' option");
> @@ -582,6 +596,7 @@ out:
>      curl_easy_cleanup(state->curl);
>      state->curl = NULL;
>  out_noclean:
> +    g_free(s->cookie);
>      g_free(s->url);
>      qemu_opts_del(opts);
>      return -EINVAL;
> @@ -684,6 +699,7 @@ static void curl_close(BlockDriverState *bs)
>      DPRINTF("CURL: Close\n");
>      curl_detach_aio_context(bs);
>  
> +    g_free(s->cookie);
>      g_free(s->url);
>  }
>  
> diff --git a/qemu-options.hx b/qemu-options.hx
> index c573dd8..7b4a58a 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -2351,6 +2351,11 @@ multiple of 512 bytes. It defaults to 256k.
>  @item sslverify
>  Whether to verify the remote server's certificate when connecting over SSL. It
>  can have the value 'on' or 'off'. It defaults to 'on'.
> +
> +@item cookie
> +Send this cookie (it can also be a list of cookies separated by ';') with
> +each outgoing request.  Only supported when using protocols such as HTTP
> +which support cookies, otherwise ignored.
>  @end table
>  
>  Note that when passing options to qemu explicitly, @option{driver} is the value
>
Eric Blake Aug. 29, 2014, 3:10 p.m. UTC | #2
On 08/29/2014 09:03 AM, Richard W.M. Jones wrote:
> In order to access VMware ESX efficiently, we need to send a session
> cookie.  This patch is very simple and just allows you to send that
> session cookie.  It punts on the question of how you get the session
> cookie in the first place, but in practice you can just run a `curl'
> command against the server and extract the cookie that way.
> 

> +++ b/qemu-options.hx
> @@ -2351,6 +2351,11 @@ multiple of 512 bytes. It defaults to 256k.
>  @item sslverify
>  Whether to verify the remote server's certificate when connecting over SSL. It
>  can have the value 'on' or 'off'. It defaults to 'on'.
> +
> +@item cookie
> +Send this cookie (it can also be a list of cookies separated by ';') with
> +each outgoing request.  Only supported when using protocols such as HTTP
> +which support cookies, otherwise ignored.

';' has to be quoted to enter it in the shell command line (but then
again, the cookie probably contains literal " which also has to be quoted).

We still don't have a QMP mapping for curl device hotplug.  But when we
gain one, do we really want to have a single (long) string containing
multiple cookies, or would it be better to make this an array argument?
 On the command-line, which is nicer, taking the cookie option multiple
times ('file.cookie=xyz,file.cookie.abc'), taking it as an automatic
array ('file.cookie.0=xyz,file.cookie.1=abc') or forcing the user to
cram all cookies into a single option ('file.cookie="xyz;abc"')?
Richard W.M. Jones Aug. 29, 2014, 3:28 p.m. UTC | #3
On Fri, Aug 29, 2014 at 09:10:10AM -0600, Eric Blake wrote:
> On 08/29/2014 09:03 AM, Richard W.M. Jones wrote:
> > In order to access VMware ESX efficiently, we need to send a session
> > cookie.  This patch is very simple and just allows you to send that
> > session cookie.  It punts on the question of how you get the session
> > cookie in the first place, but in practice you can just run a `curl'
> > command against the server and extract the cookie that way.
> > 
> 
> > +++ b/qemu-options.hx
> > @@ -2351,6 +2351,11 @@ multiple of 512 bytes. It defaults to 256k.
> >  @item sslverify
> >  Whether to verify the remote server's certificate when connecting over SSL. It
> >  can have the value 'on' or 'off'. It defaults to 'on'.
> > +
> > +@item cookie
> > +Send this cookie (it can also be a list of cookies separated by ';') with
> > +each outgoing request.  Only supported when using protocols such as HTTP
> > +which support cookies, otherwise ignored.
> 
> ';' has to be quoted to enter it in the shell command line (but then
> again, the cookie probably contains literal " which also has to be quoted).
> 
> We still don't have a QMP mapping for curl device hotplug.  But when we
> gain one, do we really want to have a single (long) string containing
> multiple cookies, or would it be better to make this an array argument?
>  On the command-line, which is nicer, taking the cookie option multiple
> times ('file.cookie=xyz,file.cookie.abc'), taking it as an automatic
> array ('file.cookie.0=xyz,file.cookie.1=abc') or forcing the user to
> cram all cookies into a single option ('file.cookie="xyz;abc"')?

For my immediate needs, I don't care at all about multiple cookies.
It's just a side-effect of the CURL API that they would work here.
I'm happy to drop all references to them from the documentation ...

Rich.
Matthew Booth Aug. 29, 2014, 3:31 p.m. UTC | #4
On 29/08/14 16:10, Eric Blake wrote:
> On 08/29/2014 09:03 AM, Richard W.M. Jones wrote:
>> In order to access VMware ESX efficiently, we need to send a session
>> cookie.  This patch is very simple and just allows you to send that
>> session cookie.  It punts on the question of how you get the session
>> cookie in the first place, but in practice you can just run a `curl'
>> command against the server and extract the cookie that way.
>>
> 
>> +++ b/qemu-options.hx
>> @@ -2351,6 +2351,11 @@ multiple of 512 bytes. It defaults to 256k.
>>  @item sslverify
>>  Whether to verify the remote server's certificate when connecting over SSL. It
>>  can have the value 'on' or 'off'. It defaults to 'on'.
>> +
>> +@item cookie
>> +Send this cookie (it can also be a list of cookies separated by ';') with
>> +each outgoing request.  Only supported when using protocols such as HTTP
>> +which support cookies, otherwise ignored.
> 
> ';' has to be quoted to enter it in the shell command line (but then
> again, the cookie probably contains literal " which also has to be quoted).
> 
> We still don't have a QMP mapping for curl device hotplug.  But when we
> gain one, do we really want to have a single (long) string containing
> multiple cookies, or would it be better to make this an array argument?
>  On the command-line, which is nicer, taking the cookie option multiple
> times ('file.cookie=xyz,file.cookie.abc'), taking it as an automatic
> array ('file.cookie.0=xyz,file.cookie.1=abc') or forcing the user to
> cram all cookies into a single option ('file.cookie="xyz;abc"')?

I thought about this, too. We're really only passing on curl's cookie
syntax:

http://curl.haxx.se/libcurl/c/CURLOPT_COOKIE.html

So even if we did it differently, the driver would still have to
reconstruct this string with ';' separation and handle escaping issues.

I doubt this will be a commonly used option, and even less frequently
used with multiple cookies, if ever[1]. Given that it is possible to use
multiple cookies without massive effort, I think the substantially
simpler code is a reasonable trade-off.

Matt

[1] Feel free to lart me with this at a later date ;)
Eric Blake Aug. 29, 2014, 3:39 p.m. UTC | #5
On 08/29/2014 09:28 AM, Richard W.M. Jones wrote:
> On Fri, Aug 29, 2014 at 09:10:10AM -0600, Eric Blake wrote:
>> On 08/29/2014 09:03 AM, Richard W.M. Jones wrote:
>>> In order to access VMware ESX efficiently, we need to send a session
>>> cookie.  This patch is very simple and just allows you to send that
>>> session cookie.  It punts on the question of how you get the session
>>> cookie in the first place, but in practice you can just run a `curl'
>>> command against the server and extract the cookie that way.
>>>

>>
>> We still don't have a QMP mapping for curl device hotplug.  But when we
>> gain one, do we really want to have a single (long) string containing
>> multiple cookies, or would it be better to make this an array argument?
>>  On the command-line, which is nicer, taking the cookie option multiple
>> times ('file.cookie=xyz,file.cookie.abc'), taking it as an automatic
>> array ('file.cookie.0=xyz,file.cookie.1=abc') or forcing the user to
>> cram all cookies into a single option ('file.cookie="xyz;abc"')?
> 
> For my immediate needs, I don't care at all about multiple cookies.
> It's just a side-effect of the CURL API that they would work here.
> I'm happy to drop all references to them from the documentation ...

Okay, given that we are treating the string as an opaque passthrough to
keep curl happy, and not something we are processing internally, I'm
happy with keeping it as a single (long) string, and just simplifying
the docs to mention that it is a string for curl to use, perhaps without
needing to go into details on whether it allows one vs. multiple cookies.
diff mbox

Patch

diff --git a/block/curl.c b/block/curl.c
index f59615d..fa1a258 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -71,6 +71,7 @@  static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
 #define CURL_BLOCK_OPT_URL       "url"
 #define CURL_BLOCK_OPT_READAHEAD "readahead"
 #define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
+#define CURL_BLOCK_OPT_COOKIE    "cookie"
 
 struct BDRVCURLState;
 
@@ -109,6 +110,7 @@  typedef struct BDRVCURLState {
     char *url;
     size_t readahead_size;
     bool sslverify;
+    char *cookie;
     bool accept_range;
     AioContext *aio_context;
 } BDRVCURLState;
@@ -382,6 +384,9 @@  static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
         curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
                          (long) s->sslverify);
+        if (s->cookie) {
+            curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
+        }
         curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
         curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
                          (void *)curl_read_cb);
@@ -489,6 +494,11 @@  static QemuOptsList runtime_opts = {
             .type = QEMU_OPT_BOOL,
             .help = "Verify SSL certificate"
         },
+        {
+            .name = CURL_BLOCK_OPT_COOKIE,
+            .type = QEMU_OPT_STRING,
+            .help = "Pass the cookie or list of cookies with each request"
+        },
         { /* end of list */ }
     },
 };
@@ -501,6 +511,7 @@  static int curl_open(BlockDriverState *bs, QDict *options, int flags,
     QemuOpts *opts;
     Error *local_err = NULL;
     const char *file;
+    const char *cookie;
     double d;
 
     static int inited = 0;
@@ -527,6 +538,9 @@  static int curl_open(BlockDriverState *bs, QDict *options, int flags,
 
     s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
 
+    cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
+    s->cookie = g_strdup(cookie);
+
     file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
     if (file == NULL) {
         error_setg(errp, "curl block driver requires an 'url' option");
@@ -582,6 +596,7 @@  out:
     curl_easy_cleanup(state->curl);
     state->curl = NULL;
 out_noclean:
+    g_free(s->cookie);
     g_free(s->url);
     qemu_opts_del(opts);
     return -EINVAL;
@@ -684,6 +699,7 @@  static void curl_close(BlockDriverState *bs)
     DPRINTF("CURL: Close\n");
     curl_detach_aio_context(bs);
 
+    g_free(s->cookie);
     g_free(s->url);
 }
 
diff --git a/qemu-options.hx b/qemu-options.hx
index c573dd8..7b4a58a 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2351,6 +2351,11 @@  multiple of 512 bytes. It defaults to 256k.
 @item sslverify
 Whether to verify the remote server's certificate when connecting over SSL. It
 can have the value 'on' or 'off'. It defaults to 'on'.
+
+@item cookie
+Send this cookie (it can also be a list of cookies separated by ';') with
+each outgoing request.  Only supported when using protocols such as HTTP
+which support cookies, otherwise ignored.
 @end table
 
 Note that when passing options to qemu explicitly, @option{driver} is the value