I have been trying to enable CORS headers on Google app engine but none of the methods that I found over the internet worked for me.
My application is on Python/Django and I want my frontend application (which is hosted separately) to be able to make API calls to my backend platform on Google App Engine.
The January 2017 release notes say that
We are changing the behavior of the Extensible Service Proxy (ESP) to deny cross-origin resource sharing (CORS) requests by default
It can be seenhere
And the solution to enable CORS given by them is to add the following snippet to the service's OpenAPI configuration.
"host": "echo-api.endpoints.YOUR_PROJECT_ID.cloud.goog",
"x-google-endpoints": [
{
"name": "echo-api.endpoints.YOUR_PROJECT_ID.cloud.goog",
"allowCors": "true"
}
],
...
So I followed this example and created two files in my code base
openapi.yml :
swagger: "2.0"
info:
description: "Google Cloud Endpoints APIs"
title: "APIs"
version: "1.0.0"
host: "echo-api.endpoints.<PROJECT-ID>.cloud.goog"
x-google-endpoints:
- name: "echo-api.endpoints.<PROJECT-ID>.cloud.goog"
allowCors: "true"
paths:
"/api/v1/sign-up":
post:
description: "Sends an email for verfication"
operationId: "signup"
produces:
- "application/json"
responses:
200:
description: "OK"
parameters:
- description: "Email address of the user"
in: body
name: email
required: true
schema:
type: string
- description: "password1"
in: body
name: password1
required: true
schema:
type: string
- description: "password2"
in: body
name: password2
required: true
schema:
type: string
openapi-appengine.yml:
swagger: "2.0"
info:
description: "Google Cloud Endpoints API fo localinsights backend server"
title: "Localinsights APIs"
version: "1.0.0"
host: "<PROJECT-ID>.appspot.com"
Then I ran this command:
gcloud service-management deploy openapi.yml
Then I edited my app.yml file to make it look like this (The addition was endpoints_api_service. Before adding this, the app was getting deployed without any errors):
runtime: python
env: flex
entrypoint: gunicorn -b :$PORT myapp.wsgi
beta_settings:
cloud_sql_instances: <cloud instance>
runtime_config:
python_version: 3
automatic_scaling:
min_num_instances: 1
max_num_instances: 1
resources:
cpu: 1
memory_gb: 0.90
disk_size_gb: 10
env_variables:
DJANGO_SETTINGS_MODULE: myapp.settings.staging
DATABASE_URL: <dj-database-url>
endpoints_api_service:
name: "<PROJECT-ID>.appspot.com"
config_id: "<CONFIG-ID>"
Then I just deployed the application with
gcloud app deploy
Now, the app got deployed successfully but it is behaving strangely. All the requests which are supposed to return a 200 response still throw CORS error but the ones which return a 400 status do work.
For example - The sign up API expects these fields - email, password1, password2 where password1 should be same as password2. Now when I send correct parameters, I get HTTP 502 saying
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin {origin-url} is therefore not allowed access. The response had HTTP status code 502
But when I send password1 not same as password2, I get HTTP 400 response which I am sure is coming from my code because the response is a dictionary written in the code if password1 and password2 do not match. Also in this case, the headers have Access-Control-Allow-Origin as * but in the former case, that was not true
I also checked my nginx error logs and it says
*27462 upstream prematurely closed connection while reading response header
What am I doing wrong here? Is this the right way to enable CORS in GAE?
After banging my head for several days, I was able to figure out the the real problem. My database server was denying any connection to the webapp server.
Since in case of a HTTP 200 response, the webapp is supposed to make a database call, the webapp was trying to connect to the database server. This connection was taking too long and as soon as it reached beyond the NGINX's timeout time, NGINX used to send a response to the web browser with the status code as 502.
Since the 'access-control-allow-origin' header was being set from the webapp, NGINX did not set that header in its response. Hence the browser was interpreting it as a CORS denial.
As soon as I whitelisted my webapp's instance's IP address for the database server, things started running smoothly
Summary:
Update:
Just wanted to update my answer to specify the way through which you won't have to add you instance's IP to the whitelisted IP(s) of the SQL instance
Configure the DATABASES like this:
Note the HOST key in the databases. GAE has a way through which you won't have to whitelist your instance's IP but for that to work, the host should be the cloudsql-connection-string and NOT the IP of the SQL instance.
If you are not sure what's your cloudsql-connection-string, go to the Google cloud platform dashboard and select the SQL tab under the Storage section. You should see a table with a column Instance connection name. The value under this column is your cloudsql-connection-string.