After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 687757 - WIP port to SoupRequest
WIP port to SoupRequest
Status: RESOLVED FIXED
Product: gvfs
Classification: Core
Component: general
unspecified
Other All
: Normal normal
: ---
Assigned To: Tomas Bzatek
gvfs-maint
: 687758 (view as bug list)
Depends on:
Blocks: 639777
 
 
Reported: 2012-11-06 13:58 UTC by Colin Walters
Modified: 2015-03-03 15:02 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
WIP port to SoupRequest (54.73 KB, patch)
2012-11-06 13:58 UTC, Colin Walters
none Details | Review
http: Port to SoupRequester (39.41 KB, patch)
2012-12-05 16:24 UTC, Tomas Bzatek
none Details | Review
http: replace SoupInputStream with SoupRequest (59.01 KB, patch)
2012-12-12 10:54 UTC, Dan Winship
committed Details | Review
http: Simplify job failure handling (4.95 KB, patch)
2012-12-12 10:54 UTC, Dan Winship
committed Details | Review
dav: kill SoupOutputStream (21.20 KB, patch)
2012-12-12 10:54 UTC, Dan Winship
committed Details | Review
do not set application octet stream mime type (1.43 KB, patch)
2015-01-15 15:02 UTC, Ondrej Holy
committed Details | Review

Description Colin Walters 2012-11-06 13:58:20 UTC
Since libsoup no longer exports soup_message_io_cancel(), the
copy of old libsoup bits in here fails.

I don't know how to use POST with SoupRequester, so the DAV bits
are not fully ported.
Comment 1 Colin Walters 2012-11-06 13:58:22 UTC
Created attachment 228247 [details] [review]
WIP port to SoupRequest
Comment 2 Tomas Bzatek 2012-11-06 14:23:12 UTC
*** Bug 687758 has been marked as a duplicate of this bug. ***
Comment 3 Kjartan Maraas 2012-11-06 18:07:20 UTC
I'll add --disable-http to the jhbuild moduleset since it doesn't build at the moment with or without this patch.
Comment 4 Tomas Bzatek 2012-12-05 16:24:43 UTC
Created attachment 230792 [details] [review]
http: Port to SoupRequester

The original patch was on a good way, thanks for this, yet needed several tweaks. I've added SoupRequester initialization and feature registration to be able to actually use it.

Also came across weird assertion failure:

> (process:29393): libsoup-CRITICAL **: soup_session_send_request_async: assertion `use_thread_context' failed

so I had to set the "use-thread-context" property on async SoupSession to TRUE. Which I don't think it's necessarily bad thing, the code looks to be well thread-aware.

The DAV backend part needed almost no changes. I've also kept SoupOutputStream around since SoupRequest (SoupRequestHTTP) seems to be only for reading, without ability to actually push some data and open a GOutputStream channel. There are no extern library imports like soup-input-stream.c used to have, we should be on a safe side at least for the moment.
Comment 5 Dan Winship 2012-12-12 10:53:54 UTC
Hm... I thought I was cc:ed on this bug but I guess not.

Anyway, I had also started working on this, but never attached the patches because I hadn't tested them yet. My version is more complicated than Tomas's, because I tried to preserve the fact that SoupInputStream was seekable, while the stream returned from SoupRequestHTTP isn't.

This patch is actually a bit out of date, since as of a few days ago, SoupRequester is now deprecated and you can just do "soup_session_request()" instead of "soup_requester_request()"...
Comment 6 Dan Winship 2012-12-12 10:54:08 UTC
Created attachment 231336 [details] [review]
http: replace SoupInputStream with SoupRequest

Replace the hacky SoupInputStream with a new GVfsHttpInputStream that
is a wrapper around SoupRequest. (We need a wrapper stream rather than
just using SoupRequest directly because we want the stream here to be
seekable, which requires cancelling and re-sending the HTTP request
and getting a new underlying stream.)

The http and dav backends still use both a sync and an async
SoupSession, even though this is no longer necessary, since changing
this would require a lot of rewriting of code that currently works.
Comment 7 Dan Winship 2012-12-12 10:54:12 UTC
Created attachment 231337 [details] [review]
http: Simplify job failure handling

gvfsbackendhttp defined g_vfs_job_failed_from_http_status(), but
didn't export this, so gvfsbackenddav was sort of forced to
reimplement it. Fix that by exporting it as http_job_failed().
Comment 8 Dan Winship 2012-12-12 10:54:15 UTC
Created attachment 231338 [details] [review]
dav: kill SoupOutputStream

SoupOutputStream was never particularly useful, since we ended up not
doing chunked requests (since server support for them is mostly
nonexistent). So kill SoupOutputStream off and just use a
GMemoryOutputStream instead.
Comment 9 Tomas Bzatek 2012-12-13 14:35:31 UTC
(In reply to comment #8)
> Created an attachment (id=231338) [details] [review]
> dav: kill SoupOutputStream
> 
> SoupOutputStream was never particularly useful, since we ended up not
> doing chunked requests (since server support for them is mostly
> nonexistent). So kill SoupOutputStream off and just use a
> GMemoryOutputStream instead.

I have never paid much attention to our dav backend, but looking at the memory stream and data passing to SoupMessage, it seems to me like the old known problem of high memory usage (equal to the full transferred file size) still stands. Even if we had a way to push data to SoupMessage by blocks (i.e. callbacks supplying data), I guess we still fail to know the overall data size to be transferred at the beginning, right? Just thinking how to work around that (if the chunked requests are not an option)...
Comment 10 Tomas Bzatek 2012-12-13 14:48:38 UTC
Writing to dav doesn't work:

(process:22013): GLib-GIO-CRITICAL **: g_memory_output_stream_steal_data: assertion `g_output_stream_is_closed (G_OUTPUT_STREAM (ostream))' failed

(process:22013): libsoup-CRITICAL **: soup_message_set_request: assertion `content_type != NULL || req_length == 0' failed

and no error is returned to client either (operation still waiting for something).


And trying to read from http makes the backend segfault trying to print assertion failure in 
  • #7 g_vfs_http_input_stream_ensure_request
    at gvfshttpinputstream.c line 118

117)       priv->req = soup_requester_request_uri (priv->requester, priv->uri, &error);
118)       g_assert_no_error (error);


Looking into it.
Comment 11 Tomas Bzatek 2012-12-13 15:19:38 UTC
Review of attachment 231336 [details] [review]:

Also, for some reason I'm getting the same assert like last time:

> (process:3287): libsoup-CRITICAL **: soup_session_send_request_async: assertion `use_thread_context' failed

Adding

> g_object_set (G_OBJECT (backend->session_async), "use-thread-context", TRUE, NULL);

in g_vfs_backend_http_init() would make it work. What is the deal behind this property?

::: daemon/gvfshttpinputstream.c
@@ +113,3 @@
+  if (!priv->req)
+    {
+      GError *error;

should be initialized NULL
Comment 12 Tomas Bzatek 2012-12-13 15:44:54 UTC
Review of attachment 231336 [details] [review]:

This last bit makes async file read working again, however for the third read attempt I'm getting 
> soup_requester_request_uri: assertion `SOUP_IS_REQUESTER (requester)' failed
failure which I cannot explain. Could that be related to the "use-thread-context" property I mentioned last time?

::: daemon/gvfshttpinputstream.c
@@ +214,3 @@
+  g_input_stream_clear_pending (http_stream);
+
+  if (soup_request_send_finish (SOUP_REQUEST (object), result, &error))

soup_request_send_finish() returns a GInputStream which should be stored in priv->stream (from the http_stream) pointer.
Comment 13 Dan Winship 2012-12-14 12:58:21 UTC
(In reply to comment #9)
> Even if we had a way to push data to SoupMessage by blocks (i.e.
> callbacks supplying data)

(There will eventually be "soup_request_send_with_body()", which will return a GIOStream so you can first write out the request body, and then read back the response body.)

> I guess we still fail to know the overall data size
> to be transferred at the beginning, right?

Exactly. Maybe we should add some gio API to let the caller (optionally) specify in advance how many bytes they're going to write? Especially if there are other backends it would help too...


(In reply to comment #10)
> Writing to dav doesn't work:

> And trying to read from http makes the backend segfault

yes, as mentioned before, the patch is totally untested


(In reply to comment #11)
> Also, for some reason I'm getting the same assert like last time:
> 
> > (process:3287): libsoup-CRITICAL **: soup_session_send_request_async: assertion `use_thread_context' failed
> 
> Adding
> 
> > g_object_set (G_OBJECT (backend->session_async), "use-thread-context", TRUE, NULL);
> 
> in g_vfs_backend_http_init() would make it work. What is the deal behind this
> property?

It makes SoupSession be gio-like with respect to GMainContexts (using g_main_context_get_thread_default() rather than the old behavior of using a single user-specified GMainContext). Parts of SoupRequester depend on that behavior, so you have to set it if you're going to use SoupRequester.

(In reply to comment #12)
> Review of attachment 231336 [details] [review]:
> 
> This last bit makes async file read working again, however for the third read
> attempt I'm getting 
> > soup_requester_request_uri: assertion `SOUP_IS_REQUESTER (requester)' failed
> failure which I cannot explain.

Uh... maybe a refcounting bug causing the requester to get freed when it's supposed to still be in use?
Comment 14 Tomas Bzatek 2012-12-14 14:46:57 UTC
Review of attachment 231338 [details] [review]:

These two bits make dav uploading working again.

::: daemon/gvfsbackenddav.c
@@ +2511,3 @@
+
+  length = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (stream));
+  data = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (stream));

Need to explicitly close the memory stream before stealing its data.

@@ +2514,3 @@
+  g_object_unref (stream);
+
+  soup_message_set_request (msg, NULL,

The content_type argument is required to be set if passing data in. Using "application/octet-stream" makes it work, though I'm not sure whether it's accepted by all dav servers.
Comment 15 Tomas Bzatek 2012-12-14 15:46:48 UTC
(In reply to comment #13)
> (There will eventually be "soup_request_send_with_body()", which will return a
> GIOStream so you can first write out the request body, and then read back the
> response body.)

This would be great to have, see the next paragraph.
 
> > I guess we still fail to know the overall data size
> > to be transferred at the beginning, right?
> 
> Exactly. Maybe we should add some gio API to let the caller (optionally)
> specify in advance how many bytes they're going to write? Especially if there
> are other backends it would help too...

It may help mtp and perhaps even obex backends too.

A GIO API would be nice, although in most cases we would be just fine with libsoup changes only. Given that most write operations are actually file copy ops, gvfs internally knows how many data is being transferred (i.e. for progress bars). If soup can provide a GOutputStream or callback where we can put data blocks, preceded by completing the header knowing total data size, and libsoup would gradually be sending data out keeping the connection open while waiting for more data, we would get rid of large memory consumption at all.

New GIO API (on public GOutputStream) would only help some client applications that are saving buffers from memory and know complete file size, such as gedit.

> (In reply to comment #12)
> Uh... maybe a refcounting bug causing the requester to get freed when it's
> supposed to still be in use?

Right, priv->requester in g_vfs_http_input_stream_new() should explicitly take a reference since soup_session_get_feature() owns the returned object. It was not clear on a first sight. We were unreffing it gradually in g_vfs_http_input_stream_finalize() leading to troubles.

---

With all the findings above it seems stable and passed my testing both for http and dav. Could you please post fixed patches or should I post separate patches on top of yours?
Comment 16 Dan Winship 2012-12-17 13:19:02 UTC
(In reply to comment #15)
> With all the findings above it seems stable and passed my testing both for http
> and dav. Could you please post fixed patches or should I post separate patches
> on top of yours?

Feel free to just squash your patches into mine and commit them with whatever authorship / commit message you want.
Comment 17 Tomas Bzatek 2012-12-18 13:26:53 UTC
I've corrected small mistakes in patch files directly where possible and pushed the rest fixes as separate commits. Pushed to master.

Thanks for the patches and please think about my needs in comment 15.
Comment 18 Dan Winship 2012-12-19 01:46:26 UTC
(In reply to comment #17)
> Thanks for the patches and please think about my needs in comment 15.

It's possible (but ugly) to stream the request body now, using the wrote-chunk signal to know when to append the next chunk. (tests/streaming-test.c in the libsoup sources shows how to do it from the server side; the client side is similar.) You just need to call soup_message_headers_set_content_length() beforehand so libsoup can write the correct Content-Length header.

The SoupRequest-based API for request bodies might show up for Christmas, but it's more likely that it won't make 3.8.
Comment 19 tran 2015-01-15 07:06:43 UTC
Our product implements WebDAV support and for MIME type detection, it takes Content-Type attribute first, then file extension. Since Ubuntu 13.04 (gvfs 1.16.1), all files uploaded via Nautilus to our server have Content-Type of application/octet-stream due to this commit https://git.gnome.org/browse/gvfs/commit/daemon/gvfsbackenddav.c?id=635c3b507e9082d8f8016f18cc14445a7493e456.

As work around, we use file extension to get MIME type in case Content-Type is application/octet-stream. Do you think that we should apply that work around or ask for another solution for this ticket?
Comment 20 Dan Winship 2015-01-15 13:28:56 UTC
(In reply to comment #19)
> https://git.gnome.org/browse/gvfs/commit/daemon/gvfsbackenddav.c?id=635c3b507e9082d8f8016f18cc14445a7493e456.

> The content_type argument is required to be set for soup_message_set_request() > if passing data in. Using "application/octet-stream" makes it work,

The alternative would be to just not use soup_message_set_request(); gvfs could use soup_message_body_append(msg->request_body, ...) instead.
Comment 21 Ondrej Holy 2015-01-15 15:02:44 UTC
Created attachment 294601 [details] [review]
do not set application octet stream mime type

gvfs-info shows me correct content-type after writing, not octet-stream. Maybe it depends on server configuration, but could you test this patch if it helps (it do what Dan suggested)?
Comment 22 Ross Lagerwall 2015-03-01 11:36:22 UTC
Review of attachment 294601 [details] [review]:

Of the webdav clients I tried, most didn't set this header so it appears that it is not needed. And the code is shorter :-)
Comment 23 Ondrej Holy 2015-03-03 15:01:57 UTC
Thanks for testing...
Comment 24 Ondrej Holy 2015-03-03 15:02:21 UTC
Comment on attachment 294601 [details] [review]
do not set application octet stream mime type

commit bccf6a229448ed944963095b673adc5f13649b12