Довольно часто программируя на Node.JS приходиться выполнять различного рода HTTP запросы. Для этого использую библиотеку Request, но иногда возникают ситуации, когда запрос нужно выполнить определённым образом и это становится определённой проблемой. В итоге решил написать свой маленький модуль, который решает данную проблему.

let url   = require('url'),
    http  = require('http'),
    https = require('https')

let REDIRECTS_FOLLOWED = 0

const REQUEST_TIMEOUT   = 5000,
      REDIRECTS_MAXIMUM = 5

function doRequest (_url, options = {}) {
    const lib = _url.startsWith('https') ? https : http

    if (Object.prototype.toString.call(options) != '[object Object]') {
        options = {}
    }

    if ('headers' in options == false) {
        options.headers = {}
    }

    if ('User-Agent' in options.headers == false) {
        options.headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14'
    }

    if ('data' in options && ('Content-Length' in options.headers) == false) {
        options.headers['Content-Length'] = Buffer.byteLength(data)
    }

    if ('proxy' in options) {
        let proxy = options.proxy
            proxy = proxy.split(':')

        options.host = proxy[0]
        options.port = proxy[1]
        options.path = _url
    } else {
        let parsed_url = url.parse(_url)

        options.host = parsed_url.host
        options.path = parsed_url.path
    }

    return new Promise(function(resolve, reject) {
        let request = lib.request(options, function(response) {
            if (response.statusCode >= 500) {
                request.abort()

                reject(new Error('Failed to load page, status code: ' + response.statusCode))
            }

            if (response.statusCode > 300 && response.statusCode < 400 && response.headers.location) {
                if (url.parse(response.headers.location).hostname == false) {
                    let parsed_url = url.parse(_url)

                    response.headers.location = url.resolve(parsed_url.host, response.headers.location)
                }

                if (REDIRECTS_FOLLOWED >= REDIRECTS_MAXIMUM) {
                    reject(new Error('Exceeded maximum redirects. Probably stuck in a redirect loop ' +  response.headers.location))

                    return false
                }

                REDIRECTS_FOLLOWED++

                console.log('#%d Redirect To: %s', REDIRECTS_FOLLOWED,  response.headers.location)

                doRequest(response.headers.location, options).then(function(result) {
                    resolve(result)
                }).catch(function(err) {
                    reject(err)
                })
            } else {
                let body = [];

                response.on('data', function(chunk) {
                    body.push(chunk)
                })

                response.on('end', function() {
                    resolve({
                        response: response,
                        body: body.join('')
                    })
                })
            }
        })

        request.setTimeout(REQUEST_TIMEOUT, function() {
            request.abort()

            reject(new Error('Timeout'))
        })

        request.on('error', function(err) {
            reject(err)
        })

        if ('data' in options) {
            request.write(data)
        }

        request.end()
    })
}

Пример

let opt = {
    headers: {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/55.0.2883.87 Chrome/55.0.2883.87 Safari/537.36'},
    data: 'some_string' // будет POST
}

doRequest('http://nonamez.name').then(function(result) {
    // console.log(result.response)
    // console.log(result.body)
}).catch(function(error) {
    // console.log(error)
})

Более развёрнутую версию можно найти на GitHub