nextcloud / nextcloud/server

Azure blob storage as primary storage - Content-Length HTTP header is missing

Open
#14,391 12 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

0. Needs triage 27-feedback bug feature: object storage
Dominant language
PHP
Stars
36.9k
Forks
5.2k
Avg merge
2d 3h
Merged PRs (30d)
713

Description

TLDR: Trying to use azure as primary storage. Without encryption, can't upload to azure at all. With encryption, uploads work but it duplicates all files with one encrypted and one unencrypted copy.

Steps to reproduce
  1. Configure azure blob storage as shown in https://github.com/nextcloud/server/pull/9483
  2. Test uploading files with and without the default encryption module enabled
Expected behaviour

Azure should be used, and files uploaded to blob storage

Actual behaviour

Without encryption module enabled:

Files fail to upload to azure, presumably because they are missing the content length header

{"reqId":"kdSDvQDiCcsUWMPi3FWM","level":3,"time":"2019-02-26T16:23:00+00:00","remoteAddr":"censored","user":"grant","app":"no app in context","method":"PUT","url":"\/remote.php\/dav\/files\/grant\/censored","message":{"Exception":"MicrosoftAzure\\Storage\\Common\\Exceptions\\ServiceException","Message":"Fail:\nCode: 411\nValue: Content-Length HTTP header is missing.\ndetails (if any): \ufeff<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>MissingContentLengthHeader<\/Code><Message>Content-Length HTTP header is missing.\nRequestId:00dfc2bc-901e-00ab-71ef-cd6d13000000\nTime:2019-02-26T16:22:59.9955829Z<\/Message><HeaderName>Content-Length<\/HeaderName><\/Error>.","Code":411,"Trace":[{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/microsoft\/azure-storage-common\/src\/Common\/Internal\/ServiceRestProxy.php","line":407,"function":"throwIfError","class":"MicrosoftAzure\\Storage\\Common\\Internal\\ServiceRestProxy","type":"::","args":[{"__class__":"GuzzleHttp\\Psr7\\Response"},201]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/promises\/src\/Promise.php","line":203,"function":"MicrosoftAzure\\Storage\\Common\\Internal\\{closure}","class":"MicrosoftAzure\\Storage\\Common\\Internal\\ServiceRestProxy","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/promises\/src\/Promise.php","line":174,"function":"callHandler","class":"GuzzleHttp\\Promise\\Promise","type":"::","args":[2,"*** sensitive parameter replaced ***",[{"__class__":"GuzzleHttp\\Promise\\Promise"},{"__class__":"Closure"},{"__class__":"Closure"}]]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/promises\/src\/RejectedPromise.php","line":40,"function":"GuzzleHttp\\Promise\\{closure}","class":"GuzzleHttp\\Promise\\Promise","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/promises\/src\/TaskQueue.php","line":47,"function":"GuzzleHttp\\Promise\\{closure}","class":"GuzzleHttp\\Promise\\RejectedPromise","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/guzzle\/src\/Handler\/CurlMultiHandler.php","line":98,"function":"run","class":"GuzzleHttp\\Promise\\TaskQueue","type":"->","args":[]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/guzzle\/src\/Handler\/CurlMultiHandler.php","line":125,"function":"tick","class":"GuzzleHttp\\Handler\\CurlMultiHandler","type":"->","args":[]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/promises\/src\/Promise.php","line":246,"function":"execute","class":"GuzzleHttp\\Handler\\CurlMultiHandler","type":"->","args":[true]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/promises\/src\/Promise.php","line":223,"function":"invokeWaitFn","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/promises\/src\/Promise.php","line":267,"function":"waitIfPending","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/promises\/src\/Promise.php","line":225,"function":"invokeWaitList","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/guzzlehttp\/promises\/src\/Promise.php","line":62,"function":"waitIfPending","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/microsoft\/azure-storage-blob\/src\/Blob\/BlobRestProxy.php","line":1755,"function":"wait","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/lib\/private\/Files\/ObjectStore\/Azure.php","line":107,"function":"createBlockBlob","class":"MicrosoftAzure\\Storage\\Blob\\BlobRestProxy","type":"->","args":["nextclouddata","urn:oid:636",null]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/lib\/private\/Files\/ObjectStore\/ObjectStoreStorage.php","line":452,"function":"writeObject","class":"OC\\Files\\ObjectStore\\Azure","type":"->","args":["urn:oid:636",null]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/lib\/private\/Files\/Storage\/Wrapper\/Wrapper.php","line":630,"function":"writeStream","class":"OC\\Files\\ObjectStore\\ObjectStoreStorage","type":"->","args":["files\/censored",null,null]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/lib\/private\/Files\/Storage\/Wrapper\/Wrapper.php","line":630,"function":"writeStream","class":"OC\\Files\\Storage\\Wrapper\\Wrapper","type":"->","args":["files\/censored",null,null]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/lib\/private\/Files\/Storage\/Wrapper\/Wrapper.php","line":630,"function":"writeStream","class":"OC\\Files\\Storage\\Wrapper\\Wrapper","type":"->","args":["files\/censored",null,null]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/lib\/private\/Files\/Storage\/Wrapper\/Wrapper.php","line":630,"function":"writeStream","class":"OC\\Files\\Storage\\Wrapper\\Wrapper","type":"->","args":["files\/censored",null,null]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/apps\/dav\/lib\/Connector\/Sabre\/File.php","line":169,"function":"writeStream","class":"OC\\Files\\Storage\\Wrapper\\Wrapper","type":"->","args":["files\/censored",null]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/apps\/dav\/lib\/Connector\/Sabre\/Directory.php","line":156,"function":"put","class":"OCA\\DAV\\Connector\\Sabre\\File","type":"->","args":[null]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/sabre\/dav\/lib\/DAV\/Server.php","line":1096,"function":"createFile","class":"OCA\\DAV\\Connector\\Sabre\\Directory","type":"->","args":["censoredfilename",null]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/sabre\/dav\/lib\/DAV\/CorePlugin.php","line":525,"function":"createFile","class":"Sabre\\DAV\\Server","type":"->","args":["files\/grant\/censored",null,null]},{"function":"httpPut","class":"Sabre\\DAV\\CorePlugin","type":"->","args":[{"absoluteUrl":"https:\/\/my.server.com\/remote.php\/dav\/files\/grant\/censored","__class__":"Sabre\\HTTP\\Request"},{"__class__":"Sabre\\HTTP\\Response"}]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/sabre\/event\/lib\/EventEmitterTrait.php","line":105,"function":"call_user_func_array","args":[[{"__class__":"Sabre\\DAV\\CorePlugin"},"httpPut"],[{"absoluteUrl":"https:\/\/my.server.com\/remote.php\/dav\/files\/grant\/censored","__class__":"Sabre\\HTTP\\Request"},{"__class__":"Sabre\\HTTP\\Response"}]]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/sabre\/dav\/lib\/DAV\/Server.php","line":479,"function":"emit","class":"Sabre\\Event\\EventEmitter","type":"->","args":["method:PUT",[{"absoluteUrl":"https:\/\/my.server.com\/remote.php\/dav\/files\/grant\/censored","__class__":"Sabre\\HTTP\\Request"},{"__class__":"Sabre\\HTTP\\Response"}]]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/sabre\/dav\/lib\/DAV\/Server.php","line":254,"function":"invokeMethod","class":"Sabre\\DAV\\Server","type":"->","args":[{"absoluteUrl":"https:\/\/my.server.com\/remote.php\/dav\/files\/grant\/censored","__class__":"Sabre\\HTTP\\Request"},{"__class__":"Sabre\\HTTP\\Response"}]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/apps\/dav\/lib\/Server.php","line":301,"function":"exec","class":"Sabre\\DAV\\Server","type":"->","args":[]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/apps\/dav\/appinfo\/v2\/remote.php","line":35,"function":"exec","class":"OCA\\DAV\\Server","type":"->","args":[]},{"file":"\/snap\/nextcloud\/11688\/htdocs\/remote.php","line":163,"args":["\/snap\/nextcloud\/11688\/htdocs\/apps\/dav\/appinfo\/v2\/remote.php"],"function":"require_once"}],"File":"\/snap\/nextcloud\/11688\/htdocs\/3rdparty\/microsoft\/azure-storage-common\/src\/Common\/Internal\/ServiceRestProxy.php","Line":490,"CustomMessage":"--"},"userAgent":"Mozilla\/5.0 (Windows) mirall\/2.5.1final (build 20181204) (Nextcloud)","version":"15.0.4.0"}

With encryption module enabled:

Files upload just fine! Awesome!

Except that it's actually storing each file twice in the container - once encrypted, and once unencrypted. The unencrypted copy stays there even after completely removing the file from nextcloud. The encrypted copy does get properly deleted.

I suspect that enabling the encryption module changes how the file is handled, instead of being streamed to azure, it's encrypted and a new transfer to azure happens, and that one has the proper headers?

It's so close to working properly that it's driving me crazy!

Server configuration

Operating system: Ubuntu 18.04

Web server/Database/PHP version: Everything configured by snap

Nextcloud version: 15.0.4.0

Updated from an older Nextcloud/ownCloud or fresh install: Fresh

Where did you install Nextcloud from: snap

Signing status:

Signing status
No errors have been found.

List of activated apps:

App list
Enabled:
  - accessibility: 1.1.0
  - activity: 2.8.2
  - cloud_federation_api: 0.1.0
  - dav: 1.8.1
  - encryption: 2.3.0
  - federatedfilesharing: 1.5.0
  - federation: 1.5.0
  - files: 1.10.0
  - files_external: 1.6.0
  - files_pdfviewer: 1.4.0
  - files_rightclick: 0.11.0
  - files_sharing: 1.7.0
  - files_texteditor: 2.7.0
  - files_trashbin: 1.5.0
  - files_versions: 1.8.0
  - files_videoplayer: 1.4.0
  - firstrunwizard: 2.4.0
  - gallery: 18.2.0
  - keeporsweep: 0.2.1
  - logreader: 2.0.0
  - lookup_server_connector: 1.3.0
  - nextcloud_announcements: 1.4.0
  - notes: 2.5.1
  - notifications: 2.3.0
  - oauth2: 1.3.0
  - password_policy: 1.5.0
  - provisioning_api: 1.5.0
  - quota_warning: 1.4.0
  - serverinfo: 1.5.0
  - sharebymail: 1.5.0
  - support: 1.0.0
  - survey_client: 1.3.0
  - systemtags: 1.5.0
  - twofactor_backupcodes: 1.4.1
  - twofactor_u2f: 2.1.1
  - workflowengine: 1.5.0
Disabled:
  - admin_audit
  - comments
  - cospend
  - files_downloadactivity
  - files_external_gdrive
  - files_markdown
  - files_mindmap
  - metadata
  - news
  - polls
  - quicknotes
  - ransomware_protection
  - tasks
  - terms_of_service
  - theming
  - twofactor_totp
  - user_ldap

Nextcloud configuration:

Config report
{
    "system": {
        "objectstore": {
            "class": "OC\\Files\\ObjectStore\\Azure",
            "arguments": {
                "account_name": "myazurestorageaccount",
                "account_key": "myazurestoragekey",
                "container": "nameofcontainer"
            }
        },
        "apps_paths": [
            {
                "path": "\/snap\/nextcloud\/current\/htdocs\/apps",
                "url": "\/apps",
                "writable": false
            },
            {
                "path": "\/var\/snap\/nextcloud\/current\/nextcloud\/extra-apps",
                "url": "\/extra-apps",
                "writable": true
            }
        ],
        "supportedDatabases": [
            "mysql"
        ],
        "memcache.locking": "\\OC\\Memcache\\Redis",
        "memcache.local": "\\OC\\Memcache\\Redis",
        "redis": {
            "host": "***REMOVED SENSITIVE VALUE***",
            "port": 0
        },
        "instanceid": "***REMOVED SENSITIVE VALUE***",
        "passwordsalt": "***REMOVED SENSITIVE VALUE***",
        "secret": "***REMOVED SENSITIVE VALUE***",
        "trusted_domains": [
            "cloud.emsley.ca"
        ],
        "datadirectory": "***REMOVED SENSITIVE VALUE***",
        "dbtype": "mysql",
        "version": "15.0.4.0",
        "overwrite.cli.url": "http:\/\/my.server.com",
        "dbname": "***REMOVED SENSITIVE VALUE***",
        "dbhost": "***REMOVED SENSITIVE VALUE***",
        "dbport": "",
        "dbtableprefix": "oc_",
        "mysql.utf8mb4": true,
        "dbuser": "***REMOVED SENSITIVE VALUE***",
        "dbpassword": "***REMOVED SENSITIVE VALUE***",
        "installed": true,
        "mail_smtpmode": "smtp",
        "mail_smtpsecure": "tls",
        "mail_sendmailmode": "smtp",
        "mail_from_address": "***REMOVED SENSITIVE VALUE***",
        "mail_domain": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpauthtype": "PLAIN",
        "mail_smtpauth": 1,
        "mail_smtphost": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpport": "587",
        "mail_smtpname": "***REMOVED SENSITIVE VALUE***",
        "mail_smtppassword": "***REMOVED SENSITIVE VALUE***",
        "maintenance": false,
        "twofactor_enforced": "false",
        "twofactor_enforced_groups": [],
        "twofactor_enforced_excluded_groups": []
    }
}

Are you using external storage, if yes which one: Azure

Are you using encryption: Trying to

Are you using an external user-backend, if yes which one: No

Client configuration

Browser: Tried Chrome and Firefox

Operating system: Windows 10

Logs
Web server error log
Web server error log No relevant errors
Nextcloud log (data/nextcloud.log)
Nextcloud log See above for the relevant errors
Browser log
Browser log No relevant errors

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with lib/private/Files/ObjectStore/Azure.php, especially the writeObject call at line 107 shown in the trace, then compare the upload paths with encryption enabled and disabled. Reproduce the missing Content-Length failure and verify that successful uploads do not leave an extra unencrypted blob after deletion.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, php
Domain
backend, cloud
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.