karelia / ConnectionKit

FTP/SFTP/WebDAV etc. for Cocoa. Join the ConnectionKit mailing list for information and suggestions.
http://lists.opensource.utr-software.com/listinfo.cgi/connection-opensource.utr-software.com
493 stars 72 forks source link

-createFileAtURL:... with intermediate directories set to YES doesn't seem to work? #76

Closed MrNoodle closed 9 years ago

MrNoodle commented 9 years ago

This might have broken at some point recently as I'm pretty sure it was working before. Get the following response (using WebDAV) when uploading a file with some non-existent intermediate directories. Everything up to "testsubdir" exists:

Error Domain=org.w3.http Code=404 "The operation couldn’t be completed. not found" UserInfo=0x10071a170 {NSLocalizedFailureReason=not found, response=<NSHTTPURLResponse: 0x10071a9e0> { URL: https://XXXXXXXXXXX/test/39744-uploadtest/testsubdir/testsubfile } { status code: 404, headers { Connection = close; "Content-Encoding" = gzip; "Content-Length" = 20; "Content-Type" = "text/html"; Date = "Mon, 06 Oct 2014 00:48:54 GMT"; Server = "Apache/2.2.16 (Debian)"; Vary = "Accept-Encoding"; } }}

MrNoodle commented 9 years ago

-createDirectory:... does work when setting the flag to create intermediate directories, btw.

mikeabdullah commented 9 years ago

Having a look through the code, I think this is a non-standard-compliant server. What sort is the server you're using?

Our current implementation appears to go:

  1. Try creating the file
  2. If that fails with code 405, create the intermediate directories
  3. Retry

So it looks like the server you're working with instead returns a 404, and so our implementation gives up.

MrNoodle commented 9 years ago

I don't control the server so I don't know. It's mydrive.ch. You can create a free account there and try it yourself.

I guess -createDirectory.. does something different? My current workaround I have in place is to do a createDirectory first and that seems to work.

mikeabdullah commented 9 years ago

-createDirectory… doesn't do anything different. The fallback in step 2 is actually to call that method!

Could you grab me a transcript at all, to confirm my hypothesis?

MrNoodle commented 9 years ago

Transcript below. Note that the dir "testsubdir" is the intermediate dir that should be created. The source file does exist. Maybe the bug is on the local end though?

2014-10-07 10:19:50.519 xctest[74052:802f] <?xml version="1.0" encoding="utf-8"?>
<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"><d:response><d:href>/test/74052-uploadtest/</d:href><d:propstat><d:prop><d:getlastmodified xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">Tue, 07 Oct 2014 14:18:10 +0000</d:getlastmodified><d:resourcetype><d:collection/></d:resourcetype></d:prop><d:status>HTTP/1.1 200 Ok</d:status></d:propstat></d:response><d:response><d:href>/test/74052-uploadtest/testfile</d:href><d:propstat><d:prop><d:getlastmodified xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">Tue, 07 Oct 2014 14:18:12 +0000</d:getlastmodified><d:getcontentlength>0</d:getcontentlength><d:resourcetype/><d:getetag>"fb366885ad57123e021cc62443f3cfcc8b6b24b2"</d:getetag><d:getcontenttype>application/x-empty</d:getcontenttype></d:prop><d:status>HTTP/1.1 200 Ok</d:status></d:propstat></d:response><d:response><d:href>/test/74052-uploadtest/testfile-1</d:href><d:propstat><d:prop><d:getlastmodified xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">Tue, 07 Oct 2014 14:18:14 +0000</d:getlastmodified><d:getcontentlength>0</d:getcontentlength><d:resourcetype/><d:getetag>"ec958ad59d3cefd664da716561298135ca7edc9f"</d:getetag><d:getcontenttype>application/x-empty</d:getcontenttype></d:prop><d:status>HTTP/1.1 200 Ok</d:status></d:propstat></d:response><d:response><d:href>/test/74052-uploadtest/testsubfile</d:href><d:propstat><d:prop><d:getlastmodified xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">Tue, 07 Oct 2014 14:19:10 +0000</d:getlastmodified><d:getcontentlength>0</d:getcontentlength><d:resourcetype/><d:getetag>"b279144ef46fd876aac11765fb2335d90fc94bce"</d:getetag><d:getcontenttype>application/x-empty</d:getcontenttype></d:prop><d:status>HTTP/1.1 200 Ok</d:status></d:propstat></d:response></d:multistatus>
2014-10-07 10:19:50.956 xctest[74052:303] Error  uploading item from file:///XXXXXXXXXXXXXXXX/testdir/testsubdir/testsubfile to https://XXXXXXXXX@webdav.mydrive.ch/test/74052-uploadtest/testsubdir/testsubfile: Error Domain=NSCocoaErrorDomain Code=4 "The file “testsubfile” could not be uploaded. The file doesn’t exist." UserInfo=0x10818d890 {NSLocalizedDescription=The file “testsubfile” could not be uploaded. The file doesn’t exist., NSUnderlyingError=0x10818d0a0 "The operation couldn’t be completed. not found"}
mikeabdullah commented 9 years ago

I'm a little puzzled. Is the <d:multistatus … XML the data you receive back from the server in the event of an upload failure?

MrNoodle commented 9 years ago

Actually, it seems that's from code leading up to the upload itself where it gets directory listings. Apparently, there are no transcripts of the upload which reinforces the idea that it's on the local end.

It may be claiming the source file isn't present.

source url: file:///XXXXXXXXXX/74388-uploadtest/testdir/testsubdir/testsubfile destination url: https://XXXXX@webdav.mydrive.ch/test/74388-uploadtest/testsubdir/testsubfile

"testsubfile" does exist on the local drive as does "/test/74388-uploadtest" on the server. "testsubdir" is the intermediate directory that is to be created.

mikeabdullah commented 9 years ago

But in which case, where does the 404 at the top of this case come from?! I think the best thing if you can report this is to work your way through -[CKWebDAVProtocol initForCreatingFileWithRequest:…] to spot where something unexpected happens.

MrNoodle commented 9 years ago

That may be from my rollback code after the upload. My latest run where I stepped through and specifically watched the output, I only performed the upload code and nothing was output to the transcript.

mikeabdullah commented 9 years ago

Right, so can you step through in the debugger to hunt it down?

MrNoodle commented 9 years ago

Ok, tracing through it, it does indeed get a 404 in response, which I'm guessing the server does because the intermediate directory is missing. I do see the code in response to the 409.

So, do we change the implementation to handle 404 similarly? My workaround of doing a create first does work so I can leave that in though it would be nice if this was supported further down.

mikeabdullah commented 9 years ago

My inclination is to be lenient and treat 404 codes the same as 405. Sure it's not to spec, but it does seem a reasonable thing to return.

One other aspect to watch out for though: WebDAV is forced to handle this case by uploading the file, and then if that fails, it needs to make the directories, and do the upload again. If the file is rather large, that could be quite a waste of time and bandwidth. It might be in your interest to perform directory creation first, to avoid that anyway.

MrNoodle commented 9 years ago

Interesting. So, I'll keep my workaround in place anyways. It's efficient since in my pre-flighting, I'm checking for the existence of directories up to the file itself so I only do it if the directories are missing.