-
Notifications
You must be signed in to change notification settings - Fork 29
/
submit.js
157 lines (143 loc) · 5.19 KB
/
submit.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* eslint node/no-deprecated-api:off */
'use strict';
var urlParser = require('url');
var typeOf = require('type-of');
var path = require('path');
var each = require('foreach');
var tools = require('../tools');
module.exports = function (cheerio, Client) {
/**
* form要素からの送信をエミュレート
*
* @param param 疑似設定するフォーム送信パラメータ
* @param callback リクエスト完了時のコールバック関数(err, response, body(buffer))
*/
var emulateSubmit = function (elem, param, callback) {
if (param === 'sync' || typeOf(param) === 'function') {
callback = param;
param = {};
}
param = param || {};
var doc = tools.documentInfo(elem);
var cli = new Client(doc._instance);
var $form = null;
// form要素でなければエラー
try {
if (elem.length === 0) {
throw new Error('no elements');
}
// 複数ある場合は先頭のフォームのみ
$form = elem.eq(0);
if (!$form.is('form')) {
throw new Error('element is not form');
}
} catch (e) {
return cli.error(e.message, {
param: { uri: doc.url },
callback: callback
});
}
// methodとURL確定
var method = ($form.attr('method') || 'GET').toUpperCase();
var url = urlParser.resolve(doc.url, $form.attr('action') || '');
// フォーム送信パラメータ作成
// 1. デフォルトパラメータ($form.field())を取得した後に
// 2. 引数で指定したパラメータ(param)で上書き
var formParam = {};
var uploadFiles = {};
var uploadTypes = {};
each([$form.field(), param], function (fp) {
each(fp, function (val, name) {
var fparam = tools.paramFilter(val);
var $elem = $form.find('[name="' + name + '"]');
if ($elem.attr('type') === 'file') {
// file要素は別管理
uploadTypes[name] = $elem.attr('multiple');
if (fparam.length > 0) {
uploadFiles[name] = uploadFiles[name] || [];
var files = typeOf(val) === 'array' ? val : val.split(',');
each(files, function (v) {
uploadFiles[name].push(path.isAbsolute(v) ? v : path.join(process.cwd(), v));
});
}
return;
}
var fvalue = typeOf(fparam) === 'array' ? fparam : [fparam];
// 空パラメータでもname=のみで送信するための仕込み
if (fvalue.length === 0) {
fvalue.push('');
}
formParam[name] = fvalue;
});
});
// アップロードファイルのチェック
var fileError = null;
each(uploadTypes, function (val, key) {
if (val !== 'multiple' && uploadFiles[key].length > 1) {
fileError = 'this element does not accept multiple files';
}
});
if (fileError) {
return cli.error(fileError, {
param: { uri: doc.url },
callback: callback
});
}
// 各種エンコーディングに対応したURLエンコードをする必要があるのでパラメータ文字列を自力で作成
var formParamArray = [];
var multiPartParam = {};
// accept-charsetは先頭で指定されているものを使用
var enc = ($form.attr('accept-charset') || doc.encoding).split(/[\s,]+/)[0];
each(formParam, function (val, name) {
formParamArray.push(
val
.map(function (vv, i) {
// multipart/form-data用のパラメータ
var mName = name;
if (val.length > 1 && mName.indexOf('[]') !== -1) {
mName = mName.replace(/\[\]/, '[' + i + ']');
}
var bufName = Client.encoding.convert(enc, mName, true);
var bufVal = Client.encoding.convert(enc, vv, true);
multiPartParam[bufName] = bufVal;
// x-www-form-urlencoded用のパラメータ
var escName = Client.encoding.escape(enc, name);
var escVal = Client.encoding.escape(enc, vv);
if (method === 'POST') {
// application/x-www-form-urlencodedでは半角スペースは%20ではなく+にする
escName = escName.replace(/%20/g, '+');
escVal = escVal.replace(/%20/g, '+');
}
return escName + '=' + escVal;
})
.join('&')
);
});
var formParamStr = formParamArray.join('&');
// GETの場合はURLに繋げてパラメータを空にする(そうしないと上手く動かないケースがたまにあった)
if (method === 'GET') {
var join = url.indexOf('?') === -1 ? '?' : '&';
if (formParamStr.length > 0) {
url += join + formParamStr;
}
formParamStr = {};
}
var postData =
Object.keys(uploadFiles).length > 0
? new tools.SubmitParams(multiPartParam, uploadFiles)
: formParamStr;
return cli.run(method, url, postData, null, callback);
};
/**
* 非同期フォーム送信
*/
cheerio.prototype.submit = function (param, callback) {
return emulateSubmit(this, param, callback);
};
/**
* 同期フォーム送信
*/
cheerio.prototype.submitSync = function (param) {
return emulateSubmit(this, param, 'sync');
};
};