I'm trying to wrap http.get with a Promise
. Here's what I've got:
import Http from 'http';
export function get(url) {
let req;
return new Promise((resolve, reject) => {
req = Http.get(url, async res => {
if(res.statusCode !== 200) {
return reject(new Error(`Request failed, got status ${res.statusCode}`));
}
let contentLengthStr = res.headers['content-length'];
if(contentLengthStr) {
let contentLength = parseInt(contentLengthStr, 10);
if(contentLength >= 0 && contentLength <= 2*1024**3) {
let buf = Buffer.allocUnsafe(contentLength);
let offset = 0;
res.on('data', chunk => {
if(chunk.length + offset > contentLength) {
return reject(new Error(`Received too much data, expected ${contentLength} bytes`));
}
chunk.copy(buf, offset);
offset += chunk.length;
});
res.on('end', () => {
if(offset === contentLength) {
resolve(buf);
} else {
return reject(new Error(`Expected ${contentLength} bytes, received ${offset}`));
}
})
} else {
return reject(new Error(`Bad Content-Length header: ${contentLengthStr}`));
}
} else {
return reject(new Error(`Missing Content-Length header not supported`));
}
});
}).catch(err => {
req.abort();
throw err;
})
}
It seems to work OK, but it feels a bit clunky.
Firstly, async
/await
appear to be of no help here. I can't throw nor return inside of res.on('end'
. Returning just returns out of the end
callback, but there's no real way for me to break out of the Http.get(url, res => {
function from inside there. throw
ing doesn't "bubble up" to the Promise I created either because the data
/end
events don't fire synchronously. I have to call reject
manually.
The part that really bothers me though is that if the server sends me more data than they said they were going to send me via the Content-Length
header, I want to abort the request and stop listening for events. To do that I've rejected the Promise, and then immediately catch it so that I can abort the request, and then I rethrow the error so that the caller can handler it. To do this I have to declare the req
variable above the Promise, and then initialise it inside the Promise, so that I can access it after the Promise.
The flow of everything just feels really clunky. Is there a nicer way to write this? (Assume I have available all ES6/ES2017+/next features)
via mpen
No comments:
Post a Comment