Home > Software engineering >  How to get response Headers from a https module in node.js
How to get response Headers from a https module in node.js

Time:03-09

all. I created a function to get the response headers of a request I made using https from node.js. But I am having trouble returning the headers value for this function.. the function is written bellow. Could you guys help me?

function getHeaders() {
  let headers = {};
  var options = {
    method: 'get',
    host: 'www.google.com',}

  const req = https.request(options, (res) => {
    let body = '';

    headers = res.headers;
    res.on('data', (chunk) => {
       body  = chunk;
       })
    res.on('end', () => {
      console.log('Body: ', body);
    })
  })
  req.end();
  return headers;
};

const headers = getHeaders();

CodePudding user response:

.request is a stream, and node.js runs asynchronously, so it returns headers, before it's assigned and before the request is done

you need to wait for request to finish, and then return values you need, which can be achieved with async/await

so, you could wrap request in a promise, and resolve when the request is done, and when calling the function use async/await

try this:

function getHeaders() {

    // return a promise
    return new Promise((resolve) => {

        let headers = {};
        var options = {
            method: 'get',
            host: 'www.google.com',
        }

        const req = https.request(options, (res) => {
            let body = '';

            headers = res.headers;
            res.on('data', (chunk) => {
                body  = chunk;
            })
            res.on('end', () => {
                console.log('Body: ', body);
                // resolve when request is done
                resolve(headers);
            })
        })
        req.end();
    });
};

// async function, awaits request to finish/resolve
(async() => {
    // get the promise and wait until it resolves
    const headers = await getHeaders();
    console.log(headers);
})();

CodePudding user response:

The problem

Your problem is that https.request() executes asynchronously thus the request is made and the next instructions are immediately executed which leads to the initial value of headers which is {} being returned.

As network is slower than executing the next instruction the callback which would set headers to the actual response headers is executed after you have already returned and therefore does not affect the result. You can test this by printing the result of getHeaders(). You will notice that the result of getHeaders() will always be printed before the request body is printed. See this example based on your code with some minor edits to make the output clearer.

function getHeaders() {
    let headers = {};
    var options = {
        method: 'get',
        host: 'www.google.com',
    }

    const req = https.request(options, (res) => {
        let body = '';

        headers = res.headers;
        // print response headers as soon as they are available
        console.log("Response header:", res.headers)
        res.on('data', (chunk) => {
            body  = chunk;
        })
        res.on('end', () => {
            // for clarity of output omit this line
            // console.log('Body: ', body);
        })
    })
    req.end();
    return headers;
};
const headers = getHeaders();
// print return value of getHeaders()
console.log("Return value of getHeaders():", headers);

Expected output:

Return value of getHeaders(): {}
Response header: {
  date: 'Tue, 08 Mar 2022 15:22:37 GMT',
  expires: '-1',
  'cache-control': 'private, max-age=0',
  'content-type': 'text/html; charset=ISO-8859-1',
  ...
  'transfer-encoding': 'chunked'
}

The solution

What you need to do is wait until the headers are available and as soon as they are return the headers.

One way to do this is by using Promises and async/ await. You will then see that the return value of getHeaders() will always be printed after the response body.

function getHeaders() {
    // return a Promise instead of headers themselves
    return new Promise((resolve, reject) => {
        let headers = {};
        var options = {
            method: 'get',
            host: 'www.google.com',
        }
    
        const req = https.request(options, (res) => {
            let body = '';
    
            headers = res.headers;
            console.log("Response header:", res.headers)
            res.on('data', (chunk) => {
                body  = chunk;
            })
            res.on('end', () => {
                // for clarity of output omit this line
                // console.log('Body: ', body);
                // body is printed and headers are available => resolve promise
                resolve(headers)
            })
        })
        // in case the request fails for some reason reject the Promise
        req.on('error', err => {
            reject(err)
        })
        req.end();
        // with resolve() headers have already been made available so this return should be deleted
        // return headers;

    })
};
// now use await (await can only be used in an asynchronous function so wrap it in one)
(async () => {
    try {
        const headers = await getHeaders();
        console.log("Return value of getHeaders():", headers);
    } catch (error) {
        // promise was rejected => do some meaningful error handling here
        console.log(error)
    }
})(); 

Expected result:

Response header: {
  date: 'Tue, 08 Mar 2022 15:46:36 GMT',
  expires: '-1',
  'cache-control': 'private, max-age=0',
  'content-type': 'text/html; charset=ISO-8859-1',
  ...
  'transfer-encoding': 'chunked'
}
Return value of getHeaders(): {
  date: 'Tue, 08 Mar 2022 15:46:36 GMT',
  expires: '-1',
  'cache-control': 'private, max-age=0',
  'content-type': 'text/html; charset=ISO-8859-1',
  ...
  'transfer-encoding': 'chunked'
}

There also is a whole thread on this topic already so you might wanna take a look at it.

  • Related