pythonajaxrackspace-cloudcloudfilespyrax

How do I make Cloudfiles FormPost return the "Access-Control-Allow-Origin" header to enable CORS?


I want to enable CORS in my Rackspace CluodFiles container, so after reading the docs, I see I have to set some container metadata (I'm using Python and Pyrax):

from pyrax import cloudfiles

cloudfiles.set_container_metadata(container_name, {
    'X-Container-Meta-Access-Control-Allow-Origin': 'localhost:8000',
    'X-Container-Meta-Access-Control-Expose-Headers': 'Access-Control-Allow-Origin',
    'X-Container-Meta-Access-Control-Max-Age': '10',
})
print cloudfiles.get_container_metadata(container_name)

And I get as output:

{'x-container-meta-access-control-allow-origin': 'localhost:8000',
 'x-container-meta-access-control-expose-headers': 'Access-Control-Allow-Origin',
 'x-container-meta-access-control-max-age': '10',
 'x-container-meta-access-log-delivery': 'false'}

But the browser is not getting a Access-Control-Allow-Origin in the OPTIONS preflight request, so it cancels the AJAX call:

HTTP/1.1 401 Unauthorized
Content-Length: 131
Content-Type: text/html; charset=UTF-8
Allow: HEAD, GET, PUT, POST, COPY, OPTIONS, DELETE
X-Trans-Id: txXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Date: Wed, 13 Nov 2013 20:07:34 GMT
Connection: keep-alive

What's missing?

Thanks!


Solution

  • Rackspace has docs on how to enable CORS for a container here. Example 7.11 is a CORS Test Page that'll let you test your configuration outside of attempting file uploads.

    Test CORS Page - accepts a token and a URL to an object or container and let's you try calling an HTTP method on it.

    NOTE: I've removed the "DELETE" method as that can have undesired results (Such as deleting your container/object)

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>Test CORS</title>
      </head>
      <body>
    
        Token<br><input id="token" type="text" size="64"><br><br>
    
        Method<br>
        <select id="method">
            <option value="GET">GET</option>
            <option value="HEAD">HEAD</option>
            <option value="POST">POST</option>
            <option value="PUT">PUT</option>
        </select><br><br>
    
        URL (Container or Object)<br><input id="url" size="64" type="text"><br><br>
    
        <input id="submit" type="button" value="Submit" onclick="submit(); return false;">
    
        <pre id="response_headers"></pre>
        <p>
        <hr>
        <pre id="response_body"></pre>
    
        <script type="text/javascript">
          function submit() {
              var token = document.getElementById('token').value;
              var method = document.getElementById('method').value;
              var url = document.getElementById('url').value;
    
              document.getElementById('response_headers').textContent = null;
              document.getElementById('response_body').textContent = null;
    
              var request = new XMLHttpRequest();
    
              request.onreadystatechange = function (oEvent) {
                  if (request.readyState == 4) {
                      responseHeaders = 'Status: ' + request.status;
                      responseHeaders = responseHeaders + '\nStatus Text: ' + request.statusText;
                      responseHeaders = responseHeaders + '\n\n' + request.getAllResponseHeaders();
                      document.getElementById('response_headers').textContent = responseHeaders;
                      document.getElementById('response_body').textContent = request.responseText;
                  }
              }
    
              request.open(method, url);
              request.setRequestHeader('X-Auth-Token', token);
              request.send(null);
          }
        </script>
    
      </body>
    </html>
    

    To set these values outside of pyrax or any other SDK, I've used the following code:

    https://gist.github.com/chrisrasco/7455804

    Remember to set your username, apikey, and path to your container in the appropriate places.