title: Issue sending complete multipart/mixed request via node
body: <p>I need to POST a multipart/mixed request which does NOT use chunking. From what I've read this means I have to essentially roll the request off of <strong>http</strong> instead of using npm <strong>request</strong>.</p>
  
  <p><a href="https://stackoverflow.com/questions/23144349/non-chunked-multipart-mixed-post">This post</a> got me started, but I'm stuck. </p>
  
  <p>The first part of the request is a basic XML payload which describes a file. The second part is the file which is to be "published". </p>
  
  <p>When I call request.end() I see that the POST hits the server it's intended for. However, the response callback doesn't get hit for 15-20 seconds. When it does, I see the following: </p>
  
  <p><strong>The client failed to send a complete request before the timeout period elapsed.</strong></p>
  
  <p>Perhaps I am calculating the content-length incorrectly so the Server is waiting on me? </p>
  
  <pre><code>   //Calc size of string in XMLWriter object
      var xmlRequestSize = Buffer.byteLength(dsXml.toString());
  
      //Calculate Content-Size for each of two parts of a multi-part request
      data = fs.readFileSync("Blood Testing.tde")
      var fileRequestSize = Buffer.byteLength(data.toString());
  
      console.log(xmlRequestSize);
      console.log(fileRequestSize);  
  
      //Multi-part boundary
      var boundary = 'zxyzzy123456boundary';
  
      //Request options
      var postOptions=
          {
              hostname: tableauServer,
              port: 8888, // for proxy
              path: '/api/2.0/sites/' + siteID + '/datasources?overwrite=true',
              method: 'POST',
              headers: {
                  'X-Tableau-Auth': adminAuthToken, 
                  'Content-Length': xmlRequestSize + fileRequestSize,
                  'Content-Type': 'multipart/mixed; boundary=' + boundary
  
              }
          }
  
  
     postCallback = function(response) {
         console.log("Hit callback");
         var str = '';
        //another chunk of data has been recieved, so append it to `str`
  
        response.on('data', function (chunk) {
          str += chunk;
        });
  
        //the whole response has been recieved, so we just print it out here
        response.on('end', function () {
          console.log(str);
  
        });
      }
  
      var req_post = http.request(postOptions, postCallback);
  
      //Part 1 metadata
       req_post.write("\r\n--" + boundary + "\r\nContent-Disposition: name='request_payload'\r\nContent-Type: 'text/xml'\r\n\r\n");
  
       //Part 1 Body
       req_post.write(dsXml.toString());
  
      //Part 2 metadata
       req_post.write("\r\n--" + boundary + "\r\nname='tableau_datasource'; filename='Blood Testing.tde'\r\nContent-Type: 'application/octet-stream'\r\n\r\n");
  
      //Part 2 Body
       req_post.write(fs.createReadStream('Blood Testing.tde').toString());
  
  
      //Close Boundary
       req_post.write("\r\n--" + boundary + "--\r\n");
  
       console.log("About to .end()");               
       req_post.end();
  
      // callback(null);   
  
      }
  </code></pre>
  
  <p>Any assistance on how to calculate this puppy and/or anything else I've done wrong would be appreciated.</p>
  
  <p>...and after breaking out Fiddler, it looks like none of the stuff I want to add via request.write are making it INTO the request. Fiddler complains: </p>
  
  <p><img src="https://i.stack.imgur.com/eJH0j.gif" alt="enter image description here"></p>
  
  <p>...and if I look at the raw request, there is nothing there but the header. Odd. Ideas?</p>
  
  <p><strong>Edit #2: Final Solution</strong></p>
  
  <pre><code>// Simple upload of a TDE file &lt; 64MB inline in the request.
  var simplePublish = function(callback) {
          //Site ID of your 'REST' site. Lookup with REST API or PGAdmin3                       
          var siteID = '98823511-cfc3-4e24-9790-27e1855fed75';
  
          //First, build the XML for the POST
          var dsXml = new XMLWriter();
          dsXml.startElement('tsRequest')
                      .startElement('datasource')
                      .writeAttribute('name', 'BloodTesting')
                          .startElement('project')
                              // ID of the Default Project in REST site. Lookup with REST API or PGAdmin3
                              .writeAttribute('id', 'a8a1c0c6-0e49-4243-a271-d49f79c7ccdb')
                          .endElement()
                      .endElement()
                  .endElement();
          console.log(dsXml.toString());
  
      var CRLF = '\r\n';
      var form = new FormData();
  
  
      form.append('request_payload', dsXml.toString(), {contentType: 'text/xml'});
      form.append('tableau_datasource', fs.createReadStream('Blood Testing.tde'),{contentType: 'application/octet-stream'});
  
      form.submit(
          {
              host: tableauServer,
              //port: 8888, // for proxy
              path: '/api/2.0/sites/' + siteID + '/datasources?overwrite=true',
              //method: 'POST',
              headers: {
                  'X-Tableau-Auth': adminAuthToken, 
                  'Content-Type': 'multipart/mixed; boundary=' + form.getBoundary()
              }
          }, function(err, res) {
                if (err) throw err;
               console.log(res.statusCode);
               var str = '';
                //another chunk of data has been recieved, so append it to `str`
  
                res.on('data', function (chunk) {
                  str += chunk;
                });
  
                //the whole response has been recieved, so we just print it out here
                res.on('end', function () {
                  console.log(str);
  
                });
  
          });
  </code></pre>