{"thread":{"id":"37063","subject":"[BUG] Push fails with dumb http to SabreDAV","startedAt":"2014-07-06T00:00:08Z","lastAt":"2014-07-12T04:46:46Z","messageCount":3,"participants":["Abbaad Haider","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"245429","messageId":"53b8918c.ca6d320a.7df8.18ac@mx.google.com","threadId":"37063","inReplyTo":null,"subject":"[BUG] Push fails with dumb http to SabreDAV","fromName":"Abbaad Haider","fromEmail":"abbaad@gmail.com","sentAt":"2014-07-06T00:00:08Z","receivedAt":"2014-07-06T00:00:08Z","isPatch":false,"sender":{"key":"abbaad@gmail.com","avatar":null},"body":"Background:\nI'm trying to host a git repository at my Box.com account, which\noffers DAV access using a server called SabreDAV. I ran into a few\nissues along the way. The first is that Box.com apparently doesn't\nlike it if a directory has a dot in the name. No big deal, just use\n\"foo\" instead of \"foo.git\" for a bare repository. Next, it gives 404\nerrors when you try to GET a URL with an extraneous query string.\nSolution: use GIT_SMART_HTTP=0. Authentication is handled by libcurl\nautomatically reading .netrc. I've gotten to the point where I can\nclone a repository, but pushing changes back to origin hangs followed\nby timeout. I don't know if this bug is in git or libcurl, but I think\nit's the former (as explained below).\n\nProblem (see also attachment):\nWhen you try to push changes to the remote, it hangs, followed by\nerroring out with \"code 52\", which is CURLE_GOT_NOTHING. Further\ninvestigation shows that cURL successfully uses basic authentication\nto GET a few files before trying a PROPFIND request on the\nrepository's root directory. The body of the request is in a separate\nXML file asking the server about the supportedlock property.\n\nThe first time, cURL sends the request without authentication and\nreceives back 401 Unauthorized. The second time it tries the same\nrequest using Basic authentication (this is the same pattern which\nworked on the earlier GET requests). It gets back a 100-Continue, but\nfails to upload the XML file like it did before (if you're following\nalong on the typescript, the verbose cURL output should say \"* We are\ncompletely uploaded and fine\"). Instead it hangs for 60 seconds and\nerrors out with \"Empty reply from server\".\n\nI don't think this bug is in libcurl, since it successfully executes\nall the other commands. I will try to investigate further, but I think\nit has something to do with the multi-pass authentication. Attached is a\ntypescript of the behavior (editorial comments are placed inside [[ ]],\nand all user info has been scrubbed). In case the attachment doesn't go\nthrough, I have also pasted the typescript to gist [1]. If anyone else\nwants to reproduce the bug, Box.com does offer a free account.\n\n[1] https://gist.githubusercontent.com/abbaad/d4a8ba1e36000eeb8e02/raw/dd5b10ae42b1936d8da33bd384c9affdb103f686/typescript.txt\n\n--\nAbbaad Haider\nabbaad@gmail.com\n\n\n$ git --version && uname -a && curl-config --version\ngit version 2.0.1\nLinux localhost 3.15.3-1-ARCH #1 SMP PREEMPT Tue Jul 1 07:32:45 CEST 2014 x86_64 GNU/Linux\nlibcurl 7.37.0\n$ mkdir foo; cd foo\n$ git --bare init\nInitialized empty Git repository in /home/user/foo/\n$ git update-server-info\n$ cd ..\n$ sudo mount -t davfs https://dav.box.com/dav ./box\n$ cp -r foo box\n$ sudo umount box\n$ rm -r foo\n$ cat > .netrc << EOF\n> machine dav.box.com\n> login [[ redacted ]]\n> password [[ redacted ]]\n> EOF\n$ chmod 600 .netrc\n$ GIT_SMART_HTTP=0 git clone https://dav.box.com/dav/foo\nCloning into 'foo'...\nwarning: You appear to have cloned an empty repository.\nChecking connectivity... done.\n$ cd foo/.git/hooks \n$ mv post-update.sample post-update        \n$ cd ../..\n$ touch bar\n$ git add .\n$ git commit -a -m \"foo bar\"                      \n[master (root-commit) 2031b47] foo bar\n 1 file changed, 0 insertions(+), 0 deletions(-)\n create mode 100644 bar\n$ GIT_SMART_HTTP=0 GIT_CURL_VERBOSE=1 GIT_TRACE=1 git push origin master\ntrace: built-in: git 'push' 'origin' 'master'\ntrace: run_command: 'git-remote-https' 'origin' 'https://dav.box.com/dav/foo'\n* Hostname was NOT found in DNS cache\n*   Trying 74.112.185.70...\n* Connected to dav.box.com (74.112.185.70) port 443 (#0)\n* successfully set certificate verify locations:\n*   CAfile: /etc/ssl/certs/ca-certificates.crt\n  CApath: none\n* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384\n* Server certificate:\n* \t subject: serialNumber=jde9iuVneOEaNkFotOvLFaWx0vb5-Qpv; C=US; ST=California; L=Los Altos; O=Box, Inc.; CN=*.box.com\n* \t start date: 2014-04-07 05:14:35 GMT\n* \t expire date: 2017-10-31 20:06:48 GMT\n* \t subjectAltName: dav.box.com matched\n* \t issuer: C=US; O=GeoTrust, Inc.; CN=GeoTrust SSL CA\n* \t SSL certificate verify ok.\n> GET /dav/foo/info/refs HTTP/1.1\nUser-Agent: git/2.0.1\nHost: dav.box.com\nAccept: */*\nAccept-Encoding: gzip\nPragma: no-cache\n\n< HTTP/1.1 401 Unauthorized\n* Server nginx is not blacklisted\n< Server: nginx\n< Date: Fri, 04 Jul 2014 23:34:18 GMT\n< Content-Type: application/xml; charset=utf-8\n< Content-Length: 242\n< Connection: keep-alive\n< Vary: Host\n< WWW-Authenticate: Basic realm=\"dav.box.com\"\n< \n* Ignoring the response-body\n* Connection #0 to host dav.box.com left intact\n* Issue another request to this URL: 'https://dav.box.com/dav/foo/info/refs'\n* Found bundle for host dav.box.com: 0x123ff40\n* Re-using existing connection! (#0) with host dav.box.com\n* Connected to dav.box.com (74.112.185.70) port 443 (#0)\n* Server auth using Basic with user '[[ redacted ]]'\n> GET /dav/foo/info/refs HTTP/1.1\nAuthorization: Basic [[ redacted ]]\nUser-Agent: git/2.0.1\nHost: dav.box.com\nAccept: */*\nAccept-Encoding: gzip\nPragma: no-cache\n\n< HTTP/1.1 200 OK\n* Server nginx is not blacklisted\n< Server: nginx\n< Date: Fri, 04 Jul 2014 23:34:19 GMT\n< Content-Type: application/octet-stream\n< Content-Length: 0\n< Connection: keep-alive\n< Vary: Host\n< Cache-control: private\n< Accept-Ranges: bytes\n< Content-Disposition: attachment;filename=\"refs\";filename*=UTF-8''refs\n< X-Content-Type-Options: nosniff\n< Last-Modified: Fri, 04 Jul 2014 23:23:51 GMT\n< ETag: \"da39a3ee5e6b4b0d3255bfef95601890afd80709\"\n< Accept-Ranges: bytes\n< \n* Connection #0 to host dav.box.com left intact\n* Found bundle for host dav.box.com: 0x123ff40\n* Re-using existing connection! (#0) with host dav.box.com\n* Connected to dav.box.com (74.112.185.70) port 443 (#0)\n* Server auth using Basic with user '[[ redacted ]]'\n> GET /dav/foo/HEAD HTTP/1.1\nAuthorization: Basic [[ redacted ]]\nUser-Agent: git/2.0.1\nHost: dav.box.com\nAccept: */*\nAccept-Encoding: gzip\nPragma: no-cache\n\n< HTTP/1.1 200 OK\n* Server nginx is not blacklisted\n< Server: nginx\n< Date: Fri, 04 Jul 2014 23:34:20 GMT\n< Content-Type: application/octet-stream\n< Content-Length: 23\n< Connection: keep-alive\n< Vary: Host\n< Cache-control: private\n< Accept-Ranges: bytes\n< Content-Disposition: attachment;filename=\"HEAD\";filename*=UTF-8''HEAD\n< X-Content-Type-Options: nosniff\n< Last-Modified: Fri, 04 Jul 2014 23:24:12 GMT\n< ETag: \"acbaef275e46a7f14c1ef456fff2c8bbe8c84724\"\n< Accept-Ranges: bytes\n< \n* Connection #0 to host dav.box.com left intact\ntrace: run_command: 'http-push' '--helper-status' 'https://dav.box.com/dav/foo/' 'refs/heads/master:refs/heads/master'\ntrace: exec: 'git' 'http-push' '--helper-status' 'https://dav.box.com/dav/foo/' 'refs/heads/master:refs/heads/master'\ntrace: exec: 'git-http-push' '--helper-status' 'https://dav.box.com/dav/foo/' 'refs/heads/master:refs/heads/master'\ntrace: run_command: 'git-http-push' '--helper-status' 'https://dav.box.com/dav/foo/' 'refs/heads/master:refs/heads/master'\n* Hostname was NOT found in DNS cache\n*   Trying 74.112.184.70...\n* Connected to dav.box.com (74.112.184.70) port 443 (#0)\n* successfully set certificate verify locations:\n*   CAfile: /etc/ssl/certs/ca-certificates.crt\n  CApath: none\n* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384\n* Server certificate:\n* \t subject: serialNumber=jde9iuVneOEaNkFotOvLFaWx0vb5-Qpv; C=US; ST=California; L=Los Altos; O=Box, Inc.; CN=*.box.com\n* \t start date: 2014-04-07 05:14:35 GMT\n* \t expire date: 2017-10-31 20:06:48 GMT\n* \t subjectAltName: dav.box.com matched\n* \t issuer: C=US; O=GeoTrust, Inc.; CN=GeoTrust SSL CA\n* \t SSL certificate verify ok.\n> PROPFIND /dav/foo/ HTTP/1.1\nUser-Agent: git/2.0.1\nHost: dav.box.com\nAccept: */*\nDepth: 0\nContent-Type: text/xml\nContent-Length: 158\nExpect: 100-continue\n\n< HTTP/1.1 100 Continue\n* We are completely uploaded and fine\n< HTTP/1.1 401 Unauthorized\n* Server nginx is not blacklisted\n< Server: nginx\n< Date: Fri, 04 Jul 2014 23:34:21 GMT\n< Content-Type: application/xml; charset=utf-8\n< Content-Length: 242\n< Connection: keep-alive\n< Vary: Host\n< WWW-Authenticate: Basic realm=\"dav.box.com\"\n* the ioctl callback returned 0\n< \n* Ignoring the response-body\n* Connection #0 to host dav.box.com left intact\n* Issue another request to this URL: 'https://dav.box.com/dav/foo/'\n* Found bundle for host dav.box.com: 0x20469c0\n* Re-using existing connection! (#0) with host dav.box.com\n* Connected to dav.box.com (74.112.184.70) port 443 (#0)\n* Server auth using Basic with user '[[ redacted ]]'\n> PROPFIND /dav/foo/ HTTP/1.1\nAuthorization: Basic [[ redacted ]]\nUser-Agent: git/2.0.1\nHost: dav.box.com\nAccept: */*\nDepth: 0\nContent-Type: text/xml\nContent-Length: 158\nExpect: 100-continue\n\n< HTTP/1.1 100 Continue\n[[ This is where it hangs for about 60 seconds ]]\n* Empty reply from server\n* Connection #0 to host dav.box.com left intact\nerror: Cannot access URL https://dav.box.com/dav/foo/, return code 52\nfatal: git-http-push failed\nerror: failed to push some refs to 'https://dav.box.com/dav/foo'\n"},{"id":"245430","messageId":"53b89d7a.030d320a.0760.206f@mx.google.com","threadId":"37063","inReplyTo":"53b8918c.ca6d320a.7df8.18ac@mx.google.com","subject":"[PATCH] http-push.c: make CURLOPT_IOCTLDATA a usable pointer","fromName":"Abbaad Haider","fromEmail":"abbaad@gmail.com","sentAt":"2014-07-06T00:43:48Z","receivedAt":"2014-07-06T00:43:48Z","isPatch":true,"sender":{"key":"abbaad@gmail.com","avatar":null},"body":"Fixes a small bug affecting push to remotes which use some sort of\nmulti-pass authentication. In particular the bug affected SabreDAV as\nconfigured by Box.com [1].\n\nIt must be a weird server configuration for the bug to have survived\nthis long. Someone should write a test for it.\n\n[1] http://marc.info/?l=git&m=140460482604482\n\nSigned-off-by: Abbaad Haider <abbaad@gmail.com>\n---\n http-push.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex f2c56c8..bd42895 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -199,7 +199,7 @@ static void curl_setup_http(CURL *curl, const char *url,\n \tcurl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);\n #ifndef NO_CURL_IOCTL\n \tcurl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);\n-\tcurl_easy_setopt(curl, CURLOPT_IOCTLDATA, &buffer);\n+\tcurl_easy_setopt(curl, CURLOPT_IOCTLDATA, buffer);\n #endif\n \tcurl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_fn);\n \tcurl_easy_setopt(curl, CURLOPT_NOBODY, 0);\n-- \n2.0.1\n"},{"id":"245883","messageId":"20140712044646.GA20675@sigill.intra.peff.net","threadId":"37063","inReplyTo":"53b89d7a.030d320a.0760.206f@mx.google.com","subject":"Re: [PATCH] http-push.c: make CURLOPT_IOCTLDATA a usable pointer","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-07-12T04:46:46Z","receivedAt":"2014-07-12T04:46:46Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Jul 05, 2014 at 08:43:48PM -0400, Abbaad Haider wrote:\n\n> Fixes a small bug affecting push to remotes which use some sort of\n> multi-pass authentication. In particular the bug affected SabreDAV as\n> configured by Box.com [1].\n\nThanks. This looks like it was caused by the refactor in ebaaf31\n(http-push: refactor curl_easy_setup madness, 2011-05-03), which moved\nthe curl_easy call into a sub-function which took the buffer as a\npointer, rather than accessing it as a local variable.\n\n> It must be a weird server configuration for the bug to have survived\n> this long. Someone should write a test for it.\n\nI think both dumb-http push-over-DAV and multi-pass authentication are\nrare, so finding a combination of the two took a while. I do not know\nenough about the server setup to know whether we could replicate this in\nour test apache setup (and nor do I particularly want to spend a lot of\ntime figuring it out for the sake of testing push-over-DAV).\n\n> diff --git a/http-push.c b/http-push.c\n> index f2c56c8..bd42895 100644\n> --- a/http-push.c\n> +++ b/http-push.c\n> @@ -199,7 +199,7 @@ static void curl_setup_http(CURL *curl, const char *url,\n>  \tcurl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);\n>  #ifndef NO_CURL_IOCTL\n>  \tcurl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);\n> -\tcurl_easy_setopt(curl, CURLOPT_IOCTLDATA, &buffer);\n> +\tcurl_easy_setopt(curl, CURLOPT_IOCTLDATA, buffer);\n\nPatch looks obviously correct.\n"}]}