2

I'm trying to remove the unnecessary pre-flight requests in my application. For it I've simplified some parts of request, removed custom headers etc. But got stuck on a problem - GET requests now work fine without pre-flights, but POST requests still have them.

I've followed the requirements:

  1. Request does not set custom HTTP headers.
  2. Content type is "text/plain; charset=utf-8".
  3. The request method has to be one of GET, HEAD or POST. If POST, content type should be one of application/x-www-form-urlencoded, multipart/form-data, or text/plain.

Both GET and POST requests go through the single httpinvoke call.

As an example - GET request that is not prefaced by pre-flight:

URL: http://mydomain/APIEndpoint/GETRequest?Id=346089&Token=f5h345

Request Method:GET

Request Headers:

Accept:*/*

Accept-Encoding:gzip, deflate

Accept-Language:uk-UA,uk;q=0.8,ru;q=0.6,en-US;q=0.4,en;q=0.2

Cache-Control:no-cache

Connection:keep-alive

Content-Type:text/plain; charset=utf-8

Host: correct host

Origin:http://localhost

Pragma:no-cache

Referer: correct referer

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36

Query String Parameters:

Id=346089

Token=f5h345

And a POST request that looks very similar but is still prefaced with pre-flight:

URL: http://mydomain/APIEndpoint/GETRequest?param=TEST

Request Method:POST

Request Headers:

Accept:*/*

Accept-Encoding:gzip, deflate

Accept-Language:uk-UA,uk;q=0.8,ru;q=0.6,en-US;q=0.4,en;q=0.2

Cache-Control:no-cache

Connection:keep-alive

Content-Length:11

Content-Type:text/plain; charset=UTF-8

Host:

Origin:http://localhost

Pragma:no-cache

Referer: User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36

Query String Parameters:

param:TEST

Request Payload

{MyData: {}}

Any advice would be appreciated! Thanks!

==== Update ===

As requested, posting the pre-flight request for the POST request:

URL: http://mydomain/APIEndpoint/GETRequest?param=TEST

Request Method:OPTIONS

Status Code:200 OK

Response Header

Access-Control-Allow-Origin:*

Cache-Control:no-cache

Content-Length:0

Date:Wed, 09 Aug 2017 08:02:16 GMT

Expires:-1

Pragma:no-cache

Server:Microsoft-IIS/8.5

X-AspNet-Version:4.0.30319

X-Powered-By:ASP.NET

Request Headers

Accept:*/*

Accept-Encoding:gzip, deflate

Accept-Language:uk-UA,uk;q=0.8,ru;q=0.6,en-US;q=0.4,en;q=0.2

Access-Control-Request-Method:POST

Cache-Control:no-cache

Connection:keep-alive

Host:correct host

Origin:http://localhost

Pragma:no-cache

Referer: correct referer

User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36

Query String Parameters

param:TEST

It's a legacy code which uses the httpinvoke library. Code snippet that actually does the call:

_converters: {
    'text json': function (input, reviver) {},
    'json text': JSON.stringify
};

headers = {
    'Content-Type': 'text/plain; charset=utf-8'
};

data = {params: "test"};

httpinvoke(url, method.toUpperCase(), {
    corsExposedHeaders: ['Content-Type'],
    headers: headers,
    input: data,
    converters: _converters,                
    inputType: 'json',
    outputType: 'json',
    timeout: self._getMessageTimeout()
}).then(function (res) {}, function (error) {});
North
  • 107
  • 1
  • 12
  • To be clear, can you explain exactly how you know the browser isn’t doing a preflight in the case of the GET request? For example, I guess you may have looked into the Network pane in your browser devtools and you see there that the browser is showing a preflight for the POST but not for the GET? And is the case of the POST request is the preflight succeeding or is your browser devtools console showing any kind of error message about the preflight? – sideshowbarker Aug 08 '17 at 22:43
  • Yes, I use DEV Tools to check the result. And yes, in case of POST pre-flight does succeed. Just to be sure, request urls are correct since they work just fine with the old version of the requests. Old versions had custom fields in headers for both GET and POST version and both generated preflight requests. – North Aug 08 '17 at 22:55
  • Thanks so to be clear, in the response to the OPTIONS request there’s no Access-Control-Allow-Headers response header? And there’s no Access-Control-Allow-Methods response header, despite the fact that the request has a Access-Control-Request-Method: POST request header? – sideshowbarker Aug 09 '17 at 08:16
  • Yes, you are correct. I've just rechecked the options request. There's no Access-Control-Allow-Headers and Access-Control-Allow-Methods. Though if I revert all the changes to the previous working solution (difference being Content-Type set to application/json and 2 custom fields in the header) - methods that you've mentioned are present. – North Aug 09 '17 at 08:24
  • OK well the only other suggestion I can offer at this point as far as trying to get some additional help with identifying what’s happening would be if you could paste in a snippet of code that shows how you’re making the request. At this point the question doesn’t mention anything at all about how you’re doing that (e.g., whether you’re using XHR or the Fetch API or an Ajax method from a JavaScript library, or what)—or whether you’ve tried the request with some other method (e.g., tried it with the Fetch API instead of XHR) but observed the same problem. – sideshowbarker Aug 09 '17 at 08:31
  • We've using a legacy library http-invoke. Added to the original post the code snippet. – North Aug 09 '17 at 08:50
  • OK personally I have zero experience or insight with httpinvoke and no idea what things it might be doing behind the scenes. I’d suggest you consider testing by rewriting the request using the fetch method—or short of that XHR—and if you can’t reproduce the same behavior with that, then it seems like the problem you’re observing is a weird quirk or bug in httpinvoke and the best place to get specific help with it would probably instead be in the https://github.com/jakutis/httpinvoke issue tracker – sideshowbarker Aug 09 '17 at 08:57
  • Well, disregarding the code that invokes the requests, There are clear guidelines in what should be in request for it to be considered "simple". So I hoped that someone could point out the parameter (or part of the data) that wasn't supposed to be there and eliminating it would remove the unneeded pre-flight request. – North Aug 09 '17 at 09:19
  • There doesn’t seem to me at least to be any parameter in that code that would trigger a preflight. But those parameters aren’t standard but are instead proprietary to httpinvoke, and nobody who isn’t already familiar with httpinvoke is going to have any idea what all the effects of those parameters might be. That’s why I think you need specific help from somebody who is actually familiar with httpinvoke – sideshowbarker Aug 09 '17 at 09:24

2 Answers2

4

This could happen if there are event listeners registered on the XMLHttpRequestUpload object (that forces a preflight; see the note on the use-CORS-preflight flag in https://xhr.spec.whatwg.org/, and a related note in https://fetch.spec.whatwg.org/ and the updated documentation on CORS preflight requests in the MDN CORS article).

Does httpinvoke do that?

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Anne
  • 7,070
  • 1
  • 26
  • 27
  • In httpinvoke code I see the following: `xhr.upload.onerror = function () { }; xhr.upload.onprogress = onuploadprogress;` And it does add an event handler. How do I format the code in comments? – North Aug 09 '17 at 09:46
  • So you're saying that if any xhr.upload event listeners are set - pre-flight request will be sent automatically? – North Aug 09 '17 at 09:54
  • Yup, that's it. – Anne Aug 09 '17 at 10:12
  • @North as far as how to format code in comments, it seems you know now you just need to put it in backticks? Or is there some other thing you wanted to do? I guess you know now that regardless, it eats all the line breaks – sideshowbarker Aug 09 '17 at 10:26
  • https://stackoverflow.com/questions/17057303/cors-request-is-preflighted-but-it-seems-like-it-should-not-be/17057853#17057853 has some related discussion – sideshowbarker Aug 09 '17 at 10:33
  • @sideshowbarker Yes, code marking works. I was wondering how to format more than one line of code. :) – North Aug 09 '17 at 11:38
  • Some notes clarifying that setting XHR event listeners triggers a preflight have been added to the https://xhr.spec.whatwg.org/ and https://fetch.spec.whatwg.org/ specs, and the information on CORS preflight requests in the MDN CORS article has also been updated to include that same information. Hopefully having this documented now in those places will save others time in the future when they run into this problem or similar ones – sideshowbarker Aug 18 '17 at 02:11
2

As @Anne mentioned the reason that POST were sending pre-flight requests despite the requests themselves conforming to the rules of "simple requests" (and thus not needing a pre-flight) was in the XMLHttpRequestUpload event listeners.

XMLHttpRequestUpload itself might not be mentioned in the code, but you can always find it in xhr.upload variable. This was the case for http-invoke library.

So totally innocent looking code like:

xhr.upload.onprogress = onuploadprogress;

actually causes mandatory pre-flight requests.

Thanks to all who helped solve this problem.

North
  • 107
  • 1
  • 12