title: CORS on pyramid server
body: <p>I am migrating a web api application from python's flask to python's pyramid, but I'm having problems when I send <code>POST</code> or <code>DELETE</code> methods to the server (<code>GET</code> works fine):</p>
  
  <blockquote>
    <p>Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at <a href="http://localhost:5002/api/index/1b5_1-auto-20180925_113130.db" rel="nofollow noreferrer">http://localhost:5002/api/index/1b5_1-auto-20180925_113130.db</a>. (Reason: CORS preflight channel did not succeed).</p>
  </blockquote>
  
  <p>The (broken) pyramid app looks like this: </p>
  
  <pre><code>from wsgiref.simple_server import make_server
  from pyramid.config import Configurator
  from pyramid.response import Response
  from pyramid.events import NewRequest
  from resultindex import ResultIndex 
  
  def add_cors_headers_response_callback(event):
      def cors_headers(request, response):
          response.headers.update({
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'POST,GET,DELETE,PUT,OPTIONS',
          'Access-Control-Allow-Headers': 'Origin, Content-Type, Accept, Authorization',
          'Access-Control-Allow-Credentials': 'true',
          'Access-Control-Max-Age': '1728000',
          })
      event.request.add_response_callback(cors_headers)
  
  def delete_result_from_index(request):
      file = request.matchdict['file']
      index = ResultIndex(rootdir)
      return index.DeleteResult(file)
  
  if __name__ == '__main__':
  
      config = Configurator()
      config.add_subscriber(add_cors_headers_response_callback, NewRequest)
  
      config.add_route(name='api-index-file-d', pattern='/api/index/{file}', request_method='DELETE')
      config.add_view(delete_result_from_index, route_name='api-index-file-d', renderer='json')
  
      app = config.make_wsgi_app()
      server = make_server('0.0.0.0', 5002, app)
      server.serve_forever()
  </code></pre>
  
  <p>And has this HTTP log: </p>
  
  <pre><code>OPTIONS /api/index/1b5_1-auto-20180925_113130.db HTTP/1.1\r\n
  Host: localhost:5002\r\n
  User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0\r\n
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
  Accept-Language: en-US,en;q=0.5\r\n
  Accept-Encoding: gzip, deflate\r\n
  Access-Control-Request-Method: DELETE\r\n
  Origin: http://localhost:8008\r\n
  Connection: keep-alive\r\n\r\n
  
  
  HTTP/1.0 404 Not Found\r\n
  Date: Thu, 11 Oct 2018 07:49:29 GMT\r\n
  Server: WSGIServer/0.2 CPython/3.5.3\r\n
  Access-Control-Allow-Methods: POST,GET,DELETE,PUT,OPTIONS\r\n
  Access-Control-Max-Age: 1728000\r\n
  Access-Control-Allow-Credentials: true\r\n
  Access-Control-Allow-Origin: *\r\n
  Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization\r\n
  Content-Type: text/html; charset=UTF-8\r\n
  Content-Length: 192\r\n\r\n
  </code></pre>
  
  <p>The same app in flask works well and looks like this:</p>
  
  <pre><code>from flask import Flask, request, jsonify
  from flask_cors import CORS
  from resultindex import ResultIndex 
  
  app = Flask(__name__)
  CORS(app)
  
  @app.route('/api/index/&lt;file&gt;', methods=['DELETE'])
  def delete_result_from_index(file):
      index = ResultIndex()
      return jsonify( index.DeleteResult(file) )
  
  if __name__ == '__main__':
      app.run(port=5002, host='0.0.0.0') #host='0.0.0.0' for public access.
  </code></pre>
  
  <p>And the (good) HTTP looks like this: </p>
  
  <pre><code>OPTIONS /api/index/1b3_1-auto-20181009_112330.db HTTP/1.1\r\n
  Host: localhost:5002\r\n
  User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0\r\n
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
  Accept-Language: en-US,en;q=0.5\r\n
  Accept-Encoding: gzip, deflate\r\n
  Access-Control-Request-Method: DELETE\r\n
  Origin: http://localhost:8008\r\n
  Connection: keep-alive\r\n\r\n
  
  
  HTTP/1.0 200 OK\r\n
  Content-Type: text/html; charset=utf-8\r\n
  Allow: OPTIONS, POST, GET, DELETE, HEAD\r\n
  Vary: Origin\r\n
  Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT\r\n
  Access-Control-Allow-Origin: http://localhost:8008\r\n
  Content-Length: 0\r\n
  Server: Werkzeug/0.14.1 Python/3.5.3\r\n
  Date: Thu, 11 Oct 2018 07:44:45 GMT\r\n\r\n
  
  
  DELETE /api/index/1b3_1-auto-20181009_112330.db HTTP/1.1\r\n
  Host: localhost:5002\r\n
  User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0\r\n
  Accept: application/json, text/plain, */*\r\n
  Accept-Language: en-US,en;q=0.5\r\n
  Accept-Encoding: gzip, deflate\r\n
  Referer: http://localhost:8008/\r\n
  Origin: http://localhost:8008\r\n
  Connection: keep-alive\r\n\r\n
  
  
  HTTP/1.0 200 OK\r\n
  Content-Type: application/json\r\n
  Content-Length: 9345\r\n
  Vary: Origin\r\n
  Access-Control-Allow-Origin: http://localhost:8008\r\n
  Server: Werkzeug/0.14.1 Python/3.5.3\r\n
  Date: Thu, 11 Oct 2018 07:44:45 GMT\r\n\r\n
  </code></pre>