Preflight request is ok, then, after auth, response does not contain allow cors header - ajax

Asp MVC 5 app deployed on IIS 8.5.
Need to enable ajax request from a number of clients.
Server-side I have In WebApiConfig.cs
config.EnableCors();
In controller:
[EnableCors(origins: "http://localhost:59901", headers: "*", methods: "*", SupportsCredentials = true)]
public class ItemController : Controller
Client side
$("#getItem").on("click", function (e) {
var myurl = "http://servername/item/details/1"
$.ajax({
url: myurl,
type: "GET",
dataType: "JSON",
xhrFields: {
withCredentials: true
},
contentType: "application/json; charset=utf-8",
error: function (jqXHR, textStatus, errorThrown) {
$('#result').text(jqXHR.responseText || textStatus);
},
success: function (result) {
$('#result').text(result);
}
});
});
Running client from VisualStudio Origin is http://localhost:59901.
Running the ajax request I get the following in fiddler:
1. Preflight request/response
OPTIONS http://vrtsrv01.webdev.local/item/details/1 HTTP/1.1
Host: vrtsrv01.webdev.local
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:59901
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Access-Control-Request-Headers: content-type
Accept: */*
Referer: http://localhost:59901/Home/Index
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,it;q=0.6,it-IT;q=0.4
HTTP/1.1 200 OK
Server: Microsoft-IIS/8.5
Access-Control-Allow-Origin: http://localhost:59901
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, MaxDataServiceVersion
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Credentials: true
X-Powered-By: ASP.NET
Date: Sat, 13 May 2017 15:34:54 GMT
Content-Length: 0
2. GET request without credentials/ 401 error response
GET http://vrtsrv01.webdev.local/item/details/1 HTTP/1.1
Host: vrtsrv01.webdev.local
Connection: keep-alive
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:59901
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Content-Type: application/json; charset=utf-8
Referer: http://localhost:59901/Home/Index
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,it;q=0.6,it-IT;q=0.4
HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Sat, 13 May 2017 15:34:54 GMT
Content-Length: 1352
Proxy-Support: Session-Based-Authentication
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>401 - Autorizzazione negata: accesso negato a causa di credenziali non valide.</title>
....
</head>
<body>
<div id="header"><h1>Errore del server</h1></div>
....
</body>
</html>
3. GET request with NTLM token for auth / response without Allow CORS header
GET http://vrtsrv01.webdev.local/item/details/1 HTTP/1.1
Host: vrtsrv01.webdev.local
Connection: keep-alive
Authorization: Negotiate <...NTLM TOKEN HERE ...>
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:59901
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Content-Type: application/json; charset=utf-8
Referer: http://localhost:59901/Home/Index
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,it;q=0.6,it-IT;q=0.4
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.5
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
Persistent-Auth: true
X-Powered-By: ASP.NET
Date: Sat, 13 May 2017 15:34:58 GMT
Content-Length: 8557
{"id":1, .....}
QUESTION
Why after enabling MVC app for CORS and seeing the right response to preflight request, the response obtained after NTLM authentication does not contain the expected Access-Control-Allow-Origin header?

I'm not sure that it will help you, but it might help someone else looking to have both NTLM and CORS enabled.
CORS enabling
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var corsAttr = new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true };
//SupportsCredentials = true means that we will add Access-Control-Allow-Credentials to the response.
config.EnableCors(corsAttr);
}
}
SupportsCredentials = true means that we will add Access-Control-Allow-Credentials to the response.
Other solutions,
global.asax.cs - properly reply with headers that allow caller from another domain to receive data
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.Request.HttpMethod == "OPTIONS")
{
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,MaxDataServiceVersion");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Context.Response.End();
}
}

SOLVED (...for now...)
The invoked action was a controller action returning Json. This does not work with CORS. I need to create an API controller, cannot use the existing controller. This leads to code duplication, but I have no time now to refactor the whole application to use a single controller both for MVC and API

Related

Why CORS error happens even if Access-Control-Allow-Origin is present in OPTIONS response in AWS lambda trigger (API Gateway of REST API)

I'm mad. Why error No 'Access-Control-Allow-Origin' header is present on the requested resource still raises even if 'Access-Control-Allow-Origin' is present in preflight response?
this is my configure:
AWS lambda function backend code (nodejs):
'use strict';
require('dotenv').config();
const mysql = require('mysql2/promise');
exports.handler = async (event, context, callback) => {
if (event.httpMethod == 'OPTIONS') {
return {
headers: {
"foo": "bar",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
statusCode: 200,
}
}
if (!event.body) {
throw new Error("no body provided")
}
const body = JSON.parse(event.body)
if (!body.statement) {
throw new Error("no statement provided")
}
const connection = await mysql.createConnection(process.env.DATABASE_URL);
const [results] = await connection.query(body.statement)
connection.end();
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
contentType: "application/json",
body: JSON.stringify(results)
};
return response;
};
this is the trigger configure (API Gateway of REST API)
API Gateway: planetscale-API
arn:aws:execute-api:ap-northeast-1:288496770745:832iyue4j2/*/*/planetscale
API endpoint: https://832iyue4j2.execute-api.ap-northeast-1.amazonaws.com/default/planetscale
Details
API type: REST
Authorization: NONE
Method: ANY
Resource path: /planetscale
Service principal: apigateway.amazonaws.com
Stage: default
Statement ID: lambda-11567109-822a-4850-abcf-14a1b0565c54
and this is the js code:
await axios.post("https://832iyue4j2.execute-api.ap-northeast-1.amazonaws.com/default/planetscale", { statement:"select * from t" });
this is what happend in chrome network
1.preflight request
Request URL: https://832iyue4j2.execute-api.ap-northeast-1.amazonaws.com/default/planetscale
Request Method: OPTIONS
Status Code: 200
Remote Address: 127.0.0.1:64646
Referrer Policy: strict-origin-when-cross-origin
access-control-allow-headers: Origin, X-Requested-With, Content-Type, Accept
access-control-allow-methods: OPTIONS,POST,GET
access-control-allow-origin: *
content-length: 0
content-type: application/json
date: Tue, 09 Aug 2022 09:24:19 GMT
x-amz-apigw-id: WlsmmH0mNjMFZGA=
x-amzn-requestid: d25ff521-94ee-48d2-bc33-68bb92495b42
x-amzn-trace-id: Root=1-62f227c3-14547cf1741c86e83d846763;Sampled=0
xxxx: aaaa
:authority: 832iyue4j2.execute-api.ap-northeast-1.amazonaws.com
:method: OPTIONS
:path: /default/planetscale
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
access-control-request-headers: content-type
access-control-request-method: POST
origin: http://localhost:8080
referer: http://localhost:8080/
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
2. POST request
Request URL: https://832iyue4j2.execute-api.ap-northeast-1.amazonaws.com/default/planetscale
Request Method: POST
Status Code: 502
Referrer Policy: strict-origin-when-cross-origin
content-length: 36
content-type: application/json
date: Tue, 09 Aug 2022 09:24:20 GMT
x-amz-apigw-id: WlsmpE_HtjMFmfw=
x-amzn-errortype: InternalServerErrorException
x-amzn-requestid: a3f991a8-1f6b-445f-b5db-697c7efaa1f8
:authority: 832iyue4j2.execute-api.ap-northeast-1.amazonaws.com
:method: POST
:path: /default/planetscale
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
content-length: 50
content-type: application/json
origin: http://localhost:8080
referer: http://localhost:8080/
sec-ch-ua: ".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
this is what happends in chrome console :
localhost/:1
Access to XMLHttpRequest at 'https://832iyue4j2.execute-api.ap-northeast-1.amazonaws.com/default/planetscale' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
App.vue?18cf:151 error aws lambda
xhr.js?ed5b:220
POST https://832iyue4j2.execute-api.ap-northeast-1.amazonaws.com/default/planetscale net::ERR_FAILED 502
dispatchXhrRequest # xhr.js?ed5b:220
xhrAdapter # xhr.js?ed5b:16
dispatchRequest # dispatchRequest.js?d0b9:58
request # Axios.js?3509:109
httpMethod # Axios.js?3509:144
wrap # bind.js?285c:9
Comment.query # models.js?a3ef:37
exec # modelsql.mjs?8304:626
download # App.vue?18cf:149
tryDownload # App.vue?18cf:200
invokeWithErrorHandling # vue.runtime.esm.js?3841:2988
invoker # vue.runtime.esm.js?3841:1786
original_1._wrapper # vue.runtime.esm.js?3841:7405

Ajax Post error 500 ONLY when passing any data

The error occurs only when I pass any
data : ,
to my ajax. I tried to put it in the URL, in an array, change the name, ... nothing change the result. If I remove data, I got a proper 200 status response.
If I access directly the page, no problem, still a clean 200.
My ajax function :
$( "#deleteMool" ).click(function() {
$.ajax({
url : '/Trunk/SofastBS/mool-delete',
type : 'POST',
data : 'mool=' + '4' ,
dataType : 'html' ,
success : function(code_html, statut){
if (code_html == 'OK') {
location.reload();
}
},
});
});
My PHP page :
<?php
$ID = $_POST['mool'];
// Check if user is logged in
if ( is_user_logged_in() ){
/*.................. Empty request block ...............*/
if ($ID != ''){
$mool_id = delete_mool( $ID ) ;
//On success
if ( $mool_id == true ) {
echo 'OK';
}
else{
echo 'Une erreur est survenue.';
}
}
else {
header('Location: '.site_url());
echo 'ID EMPTY';
}
}
else {
header('Location: '.site_url());
echo 'NOT LOGGED IN';
}
My HEADERS :
Response header :
HTTP/1.0 500 Internal Server Error
Date: Tue, 29 Oct 2019 10:19:37 GMT
Server: Apache
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Link: <http://192.168.1.95/Trunk/SofastBS/wp-json/>; rel="https://api.w.org/"
Link: <http://192.168.1.95/Trunk/SofastBS/?p=400>; rel=shortlink
Content-Length: 1
Connection: close
Content-Type: text/html; charset=UTF-8
Request header :
POST /Trunk/SofastBS/mool-delete HTTP/1.1
Host: 192.168.1.95
Connection: keep-alive
Content-Length: 6
Accept: text/html, */*; q=0.01
Origin: http://192.168.1.95
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://192.168.1.95/Trunk/SofastBS/mool/
Accept-Encoding: gzip, deflate
Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_censored_by_me; wp-settings-1=mfold%3Do; wp-settings-time-1=1572342088; PHPSESSID=65hhsmd6290lrei04se2tc8c93
In order to compare, I use the same bit of code in another web part, on another site, and work flawlessly :
Here are the headers from the working bit :
Response header :
HTTP/1.1 200 OK
Date: Tue, 29 Oct 2019 10:29:38 GMT
Server: Apache
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Link: <http://192.168.1.95/trunk/nagacount/wp-json/>; rel="https://api.w.org/"
Link: <http://192.168.1.95/trunk/nagacount/?p=79>; rel=shortlink
Content-Length: 2
Keep-Alive: timeout=5, max=95
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
Request header :
POST /trunk/nagacount/mool-delete HTTP/1.1
Host: 192.168.1.95
Connection: keep-alive
Content-Length: 5
Accept: text/html, */*; q=0.01
Origin: http://192.168.1.95
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://192.168.1.95/trunk/nagacount/mool/
Accept-Encoding: gzip, deflate
Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_censored_by_me; PHPSESSID=65hhsmd6290lrei04se2tc8c93
I'm stuck on this for a moment, I'm sure I miss something, but I can't find it out. Any hints ?

Browser auth popup not showing up in case of ajax cors with basic authentication request

From a web page of domain A, I am firing up an ajax request to domain B in order to get JSON for which basic auth is configured on domain B. I have access to the code on both the domains. I configured the all the required CORS header on domain B (Even made Access-Control-Allow-Origin header value specific and not "*", after reading some stackoverflow) What I am expecting is browser basic auth pop up, But POST request just fails with 401.I can see that server has responded with expected response header for PRE-FLIGHT OPTION request, below the request & response headers of the OPTION & actual POST method call that happens
***OPTION REQUEST***
Host: DOMAIN_B:8085
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: x-requested-with
Referer: http://DOMAIN_A:2280/app/
Origin: http://DOMAIN_A:2280
Connection: keep-alive
***OPTION RESPONSE***
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: http://DOMAIN_A:2280
Vary: Origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
Access-Control-Allow-Headers: Content-Type,Authorization,x-requested-with
Access-Control-Max-Age: 1
Allow: GET,POST
Content-Type: text/html; charset=utf-8
Content-Length: 8
Date: Fri, 04 Jan 2019 12:48:48 GMT
Connection: keep-alive
*** ACTUAL POST REQUEST***
Host: DOMAIN_B:8085
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://DOMAIN_A:2280/app/
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 105
Origin: http://DOMAIN_A:2280
Connection: keep-alive
*** ACTUAL POST REQUEST***
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Vary: X-HTTP-Method-Override, Origin
Access-Control-Allow-Origin: http://DOMAIN_A:2280
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST,GET,PUT,DELETE
Access-Control-Allow-Headers: Content-Type,Authorization,x-requested-with
Access-Control-Max-Age: 1
WWW-Authenticate: Basic realm=artist
Content-Type: text/plain; charset=utf-8
Content-Length: 12
Date: Fri, 04 Jan 2019 12:48:48 GMT
Connection: keep-alive
So its expected that browser looking at the response of the POST call(401 HTTP code & WWW-Authenticate header) should get prompted to show the native authentication pop up, But it's not doing so. I am not sure what I am doing wrong here. Showing custom form to capture the credential and passing them in "Authorization" header using btoa function is not an option
Appreciate any help, I am ripping my hair apart here!!!
use basic-auth npm plugin
const auth = require('basic-auth');
app.use(function (request, response, next) {
var user = auth(request);
console.log("user => ",user);
if (!user || !user.name || !user.pass) {
response.set('WWW-Authenticate', 'Basic realm="example"');
return response.status(401).send();
}
return next();
});

XMLHttpRequest CROS issues when uploading(post) files to S3 from browser and redirecting to a custom url

This case is easy to understand, and I have paste enough information about the problem. Thank you for your patience. :)
There is a case that I use JQuery File Upload (UI) to upload images to AWS S3 directly from client browser, here is the post data:
AWSAccessKeyId: xxxxxx,
key: filename.jpg,
Policy: xxxxxx,
Signature: xxxxx,
acl: 'private',
success_action_redirect:'http://example.org/test',
'Content-Type': x.type
the policy and signature are totally fine, and the image has been uploaded as well.
but there is problem when redirect to the pre-defined url http://example.org/test:
XMLHttpRequest cannot load https://s3-eu-west-1.amazonaws.com/mybucket/.
The request was redirected to 'http://localhost:8000/test?bucket=mybucket&key=filename.jpg&etag=xxxxxxxx',
which is disallowed for cross-origin requests that require preflight.
I paste the http request and response for https://s3-eu-west-1.amazonaws.com/mybucket/:
Request:
POST /mybucket/ HTTP/1.1
Host: s3-eu-west-1.amazonaws.com
Connection: keep-alive
Content-Length: 298856
Origin: http://localhost:8000
X-CSRF-TOKEN: H5HRwmtwCVAxIgmAvM8YL5bgayuDyyQV2UKUqnhT
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryhI9Z5605GrykYXvT
Accept: application/json, text/javascript, */*; q=0.01
Content-Disposition: attachment; filename="xxxxxxx"
Referer: http://localhost:8000/xxxxxxxx
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Response:
HTTP/1.1 303 See Other
x-amz-id-2: g1VdA6dwEHl+y/C8nSTD7qzxL7gX9o3c0JV7Cj7cKYDeUPNvlrkRzaJEz4PtNFCPZhOAhA8pqzw=
x-amz-request-id: 48C7F5DB54CCEF65
Date: Thu, 29 Oct 2015 02:35:31 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT
Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
ETag: "772d776abbc1bb619d208c92d4b986c9"
Location: http://localhost:8000/test?bucket=mybucket&key=filename.jpg&etag=xxxxxxxx
Content-Length: 0
Server: AmazonS3
And for the redirect endpoint http://example.org/test, which is implemented in Laravel 5.1. Here are the relative routes:
Route::group(['prefix' => 'test'], function () {
Route::options('/', function(){
return response(null, 204)
->header('Access-Control-Allow-Origin' , '*')
->header('Access-Control-Allow-Credentials', 'true')
->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
->header('Access-Control-Allow-Headers', 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type')
->header('Access-Control-Max-Age', '1728000')
->header('Content-Type', 'text/plain charset=UTF-8')
->header('Content-Length', '0');
});
Route::get('/', function () {
return response('test', 200)
->header('Access-Control-Allow-Origin' , '*')
->header('Access-Control-Allow-Credentials', 'true')
->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
->header('Access-Control-Allow-Headers', 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type');
});
});
When GET http://example.org/test directly, here is the HTTP response headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:*
Cache-Control:no-cache
Connection:close
Content-Type:text/html; charset=UTF-8
Date:Thu, 29 Oct 2015 02:47:51 GMT
Host:localhost:8000
Any body can help me figure out where is the problem? Thanks!

How to make Flask/ keep Ajax HTTP connection alive?

I have a jQuery Ajax call, like so:
$("#tags").keyup(function(event) {
$.ajax({url: "/terms",
type: "POST",
contentType: "application/json",
data: JSON.stringify({"prefix": $("#tags").val() }),
dataType: "json",
success: function(response) { display_terms(response.terms); },
});
I have a Flask method like so:
#app.route("/terms", methods=["POST"])
def terms_by_prefix():
req = flask.request.json
tlist = terms.find_by_prefix(req["prefix"])
return flask.jsonify({'terms': tlist})
tcpdump shows the HTTP dialog:
POST /terms HTTP/1.1
Host: 127.0.0.1:5000
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/json; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://127.0.0.1:5000/
Content-Length: 27
Pragma: no-cache
Cache-Control: no-cache
{"prefix":"foo"}
However, Flask replies without keep-alive.
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 445
Server: Werkzeug/0.8.3 Python/2.7.2+
Date: Wed, 09 May 2012 17:55:04 GMT
{"terms": [...]}
Is it really the case that keep-alive is not implemented?
The default request_handler is WSGIRequestHandler.
Before app.run(), Add one line,
WSGIRequestHandler.protocol_version = "HTTP/1.1"
Don't forget from werkzeug.serving import WSGIRequestHandler.
Werkzeug's integrated web server builds on BaseHTTPServer from Python's standard library. BaseHTTPServer seems to support Keep-Alives if you set its HTTP protocol version to 1.1.
Werkzeug doesn't do it but if you're ready to hack into the machinery that Flask uses to instantiate Werkzeug's BaseWSGIServer, you can do it yourself. See Flask.run() which calls werkzeug.serving.run_simple(). What you have to do boils down to BaseWSGIServer.protocol_version = "HTTP/1.1".
I haven't tested the solution. I suppose you do know that Flask's web server ought to be used for development only.

Resources