ladjs / superagent

Ajax for Node.js and browsers (JS HTTP client). Maintained for @forwardemail, @ladjs, @spamscanner, @breejs, @cabinjs, and @lassjs.
https://ladjs.github.io/superagent/
MIT License
16.58k stars 1.33k forks source link

Support posting multipart/form-data following a redirect #1607

Open sathishlxg opened 3 years ago

sathishlxg commented 3 years ago

When a redirect response (307 or 308) is received for a request with multipart/form-data, superagent does not post the formData when following the redirect as the formData object is deleted to prevent request hanging by this PR #848. But this prevents the formData from being posted. As a workaround we have created a plugin to save the formdata in memory and replay it when following a redirect. I'm not sure if this is the best way so can this issue be fixed in the library itself?

export default (formDataMap = new Map()) => request => {
    const _originalField = request.field;
    const _originalAttach = request.attach;

    request.field = (name, value, options) => {
        const formData = map.get(name) || [];

        formData.push({value, options, isFile: false});

        return _originalField.call(request, name, value, options);
    };

    request.attach = (name, value, options) => {
        const formData = map.get(name) || [];

        formData.push({value, options, isFile: true});

        return _originalAttach.call(request, name, value, options);
    };

    request.on('redirect', ({statusCode}) => {
        // Only for these 2 HTTP status code we need to post the form-data if any is available.
        // For other status codes, superagent follows the redirect with a GET/HEAD request.
        if ([307, 308].indexOf(statusCode) > -1 && formDataMap.size) {
            const formData = new FormData();
            const formFields = Array.from(formDataMap.keys());

            formFields.forEach(fieldName => {
                const formDataItems = formDataMap.get(fieldName);

                formDataItems.forEach(({value, options, isFile}) => {
                        // only if file path is specified, recreate a readable stream as the previous
                        // one created by superagent would have been consumed by the initial request.
                        // This should work for any number of redirects
                        if (isFile && typeof value === 'string') {
                            value = fs.createReadStream(value);
                        }

                        formData.append(fieldName, value, options);
                    });
            });

            request._formData = formData;
        }
    });

    return request;
};

Usage

    request.post(url)
        .use(superagentReplayFormData())
        .field('token', 'ABC1234ABC')
        .attach('file', __dirname + '/README.md')
        .attach('file', Buffer.from('<b>Hello world</b>'), 'hello.html');