Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setRequestHeader Content-Type causes POST request to become OPTIONS

If you don't know the answer, thumbs up.

function local_upload_photo(form_data)
{    
    var boundary    = "-----------------------------" + (new Date).getTime();
    var CRLF        = "\r\n";
    var parts       = [];

    // form text fields
    for(var i in form_data)
    {
        if(form_data.hasOwnProperty(i))
        {
            var part = 'Content-Disposition: form-data; name="' + i + '"' + CRLF + CRLF + form_data[i] + CRLF;

            parts.push(part);
        }
    }

    var data    = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQImWNgYGAAAAAEAAGjChXjAAAAAElFTkSuQmCC');

    // photo file
    var part = 'Content-Disposition: form-data; name="file1"; filename="me.gif"' + CRLF + "Content-Type: image/gif" + CRLF + CRLF + data + CRLF;

    //console.log( base64_encode(element.files[0].getAsBinary()) );

    parts.push(part);

    // prepare the query
    var request = 'Content-Type: multipart/form-data; boundary=' + boundary + CRLF + CRLF; 
        // content-length is missing    
        request += "--" + boundary + CRLF;
        request += parts.join("--" + boundary + CRLF);
        request += "--" + boundary + "--" + CRLF;

    // send the data
    var xhr      = new XMLHttpRequest();

    xhr.open('post', 'http://upload.guy.com/storage.php');

    xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
    xhr.setRequestHeader('Content-Length', String(request.length));


    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
           console.log(xhr.responseText);
        }

    };

    // finally send the request as binary data
    xhr.sendAsBinary(request);
}

Story: the user comes to guy.lt and runs the JS code he is given in URL bar using javascript:. This should upload a file, the one you see in base64 to storage.guy.lt. However, same origin policy kicks in here and does not allow it. One solution would be simply to ask people to do the same on storage.guy.lt or just move the upload guy.lt, however, client does not agree with that.

So after searching for a while I came across website Facebook. Now if you will monitor the process how FB uploads photos, you will notice that user does this from facebook.com, however POST request (as well using XMLHttpRequest, AFAIK) is sent to uploads.facebook.com. How do they do that?

In one place they load iframe http://static.ak.facebook.com/common/redirectiframe.html with content:

if (navigator && navigator.userAgent && !(parseInt((/Gecko\/([0-9]+)/.exec(navigator.userAgent) || []).pop()) <= 20060508)) {
        //document.domain='facebook.com';
    }

I have tried doing similarly in my case, but this does not seem to have anything in common.

like image 488
Gajus Avatar asked Feb 12 '26 19:02

Gajus


1 Answers

Not entirely clear what the question is but here goes:

  1. What you're seeing with the POST 'becoming' an OPTIONS request is preflighting - when making a cross-domain XHR request, the browser decides under certain circumstances (for example, when making a POST with the content-type set to something other than application/x-www-form-urlencoded, multipart/form-data, or text/plain) to first check if the request will be allowed by the server before actually making it.

  2. You haven't mentioned if you have control over the server-side of things but if you do, you have the option of responding to the OPTIONS request with

    Access-Control-Allow-Origin: http://guy.lt
    Access-Control-Allow-Methods: POST, OPTIONS
    

    to allow your JavaScript upload to go through.

  3. The approach Facebook seems to be taking is that of setting the document.domain attribute which, if the parent window (www.facebook.com) and an iframe from another server on the same domain (uploads.facebook.com) set to the same value (facebook.com), scripts from each one can talk to the other1. This can be used to do cross-[sub]domain requests to the original domain of the window or the iframe. So the parent window from www.facebook.com can call JavaScript loaded from uploads.facebook.com in the iframe which will then allow requests back to uploads.facebook.com. This blog post describes this technique in more detail.

like image 139
no.good.at.coding Avatar answered Feb 14 '26 08:02

no.good.at.coding



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!