swisspol / GCDWebServer

The #1 HTTP server for iOS, macOS & tvOS (also includes web based uploader & WebDAV server)
Other
6.51k stars 1.25k forks source link

Change file permissions on server for WebDAV operations (iOS) #545

Closed makorendev closed 3 years ago

makorendev commented 3 years ago

I'm currently trying to use GCDWebServer to host a local webpage that's requested by a WKWebView when the app starts. I want to be able to upload external files to the server by grabbing files with a UIDocumentPickerViewController while the app is running. It seems like using a separate GCDWebDAVServer on a different port is a good idea for this.

However, if I try to upload a file to the WebDAV server, I get this data from the response:

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>HTTP Error 500</title></head><body><h1>HTTP Error 500: Failed moving uploaded file to &quot;/arbitrary.txt&quot;</h1><h3>[NSCocoaErrorDomain] “35B890AC-7CD2-4A10-A67F-BAED3D6C34AB-3278-000005593C778876” couldn’t be moved because you don’t have permission to access “www”. (513)</h3></body></html>

Cutting out this bit for readability:

Failed moving uploaded file to &quot;/arbitrary.txt&quot;</h1><h3>[NSCocoaErrorDomain] “35B890AC-7CD2-4A10-A67F-BAED3D6C34AB-3278-000005593C778876” couldn’t be moved because you don’t have permission to access “www”.

www in this context is the local folder I'm serving with GCDWebServer. It has no subfolders, only files.

Here's the code I'm using in viewDidLoad:

let webContentUrl = Bundle.main.path(forResource: "www", ofType: nil)!
httpServer = GCDWebServer()

webDAVURL = "http://localhost:\(WEBDAV_PORT)/"
webDAVServer = GCDWebDAVServer(uploadDirectory: webContentUrl)

httpServer.addGETHandler(forBasePath: "/", directoryPath: webContentUrl, indexFilename: "index.html", cacheAge: 3600, allowRangeRequests: true)
httpServer.start(withPort: HTTP_PORT, bonjourName: nil)

let options: [String: Any] = [
    GCDWebServerOption_Port: WEBDAV_PORT
]

do {
    try webDAVServer.start(options: options)
} catch let error {
    print("Could not start WebDAV server. Reason: \(error.localizedDescription)")
}

let request = URLRequest(url: URL(string: "http://localhost:\(HTTP_PORT)/")!)
webView.load(request)

And the code used for a PUT request to upload a file to the WebDAV server:

let importedFileUrl = urls.first!

do {
    let data = try Data(contentsOf: importedFileUrl)

    var request = URLRequest(url: URL(string: "http://localhost:\(WEBDAV_PORT)/arbitrary.txt")!)
    request.httpMethod = "PUT"

    let task = URLSession(configuration: .ephemeral).uploadTask(with: request, from: data) { data, response, error in
        print("Data: \(String(describing: data))")
        print("Response: \(String(describing: response))")
        print("Error: \(String(describing: error))")
        print(String(decoding: data!, as: UTF8.self))
    }

    task.resume()

} catch let error {
    createErrorAlert(message: error.localizedDescription)
}

This doesn't have anything to do with iOS 14 local network privacy features. I tried modifying the Info.plist to include the new keys, but nothing changed. It seems like the www folder doesn't have write permissions.

Is there a feature I'm missing that lets you change file permissions in GCDWebServer, or is there a workaround? Right now the only workaround I can think of is to create a local database alongside the web servers.

makorendev commented 3 years ago

The reason write permissions were not allowed is because I was serving files directly from the app bundle, which cannot be written to.

My solution was to copy the contents of that directory into the Documents directory and use WebDAV to write to that directory instead.