request/index.js

258 lines
5.9 KiB
JavaScript
Raw Normal View History

2020-09-16 20:07:28 +08:00
/**
* @author yutent<yutent.io@gmail.com>
* @date 2020/09/16 16:05:57
*/
import 'es.shim'
import Parser from './lib/index.js'
2020-09-21 17:57:42 +08:00
import { parseCookie } from './lib/cookie.js'
2020-09-16 20:07:28 +08:00
import fs from 'iofs'
2023-10-26 19:02:46 +08:00
import { fileURLToPath, parse } from 'node:url'
import QS from 'node:querystring'
import { dirname, resolve } from 'node:path'
2020-09-16 20:07:28 +08:00
2020-12-17 10:36:48 +08:00
const DEFAULT_FORM_TYPE = 'application/x-www-form-urlencoded'
2023-10-26 19:02:46 +08:00
const __dirname = dirname(fileURLToPath(import.meta.url))
2020-09-28 09:48:20 +08:00
2023-10-26 19:02:46 +08:00
const tmpdir = resolve(__dirname, '.tmp/')
2023-10-25 18:45:16 +08:00
const encode = encodeURIComponent
const decode = decodeURIComponent
2020-09-16 20:07:28 +08:00
2022-01-08 16:59:11 +08:00
if (fs.isdir(tmpdir)) {
fs.rm(tmpdir, true)
}
fs.mkdir(tmpdir)
2020-09-16 20:07:28 +08:00
function hideProperty(host, name, value) {
Object.defineProperty(host, name, {
value: value,
writable: true,
enumerable: false,
configurable: true
})
}
export default class Request {
2023-10-26 19:02:46 +08:00
#req = null
#res = null
2023-10-30 16:41:37 +08:00
#opts = {}
2023-10-26 19:02:46 +08:00
#query = null
#body = null
#cookies = Object.create(null)
method = 'GET'
path = []
url = ''
host = '127.0.0.1'
2023-10-30 16:41:37 +08:00
constructor(req, res, opts = {}) {
2020-09-16 20:07:28 +08:00
this.method = req.method.toUpperCase()
2023-10-26 19:02:46 +08:00
this.#req = req
this.#res = res
this.host = req.headers['host']
this.#cookies = parseCookie(this.headers['cookie'] || '')
2023-10-30 16:41:37 +08:00
Object.assign(this.#opts, opts)
2023-10-26 19:02:46 +08:00
this.#init()
2020-09-16 20:07:28 +08:00
}
// 修正请求的url
2023-10-26 19:02:46 +08:00
#init() {
let _url = parse(this.#req.url)
2020-09-16 20:07:28 +08:00
.pathname.slice(1)
.replace(/[\/]+$/, '')
let app = '' // 将作为主控制器(即apps目录下的应用)
let pathArr = []
// URL上不允许有非法字符
2023-10-26 19:02:46 +08:00
if (/[^\w-/.,@~!$&:+'"=]/.test(decode(_url))) {
this.#res.rendered = true
this.#res.writeHead(400, {
2020-09-23 17:43:13 +08:00
'X-debug': `url [/${encode(_url)}] contains invalid characters`
2020-09-16 20:07:28 +08:00
})
2023-10-26 19:02:46 +08:00
return this.#res.end(`Invalid characters: /${_url}`)
2020-09-16 20:07:28 +08:00
}
// 修正url中可能出现的"多斜杠"
_url = _url.replace(/[\/]+/g, '/').replace(/^\//, '')
pathArr = _url.split('/')
if (!pathArr[0] || pathArr[0] === '') {
pathArr[0] = 'index'
}
if (pathArr[0].indexOf('.') !== -1) {
app = pathArr[0].slice(0, pathArr[0].indexOf('.'))
// 如果app为空(这种情况一般是url前面带了个"."造成的),则自动默认为index
if (!app || app === '') {
app = 'index'
}
} else {
app = pathArr[0]
}
pathArr.shift()
this.app = app
this.url = _url
this.path = pathArr
}
/**
2023-10-26 19:02:46 +08:00
* [解析请求体, 需要 await ]
2020-09-16 20:07:28 +08:00
* @param {Str} key [字段]
*/
2023-10-26 19:02:46 +08:00
#parseBody() {
2020-09-16 20:07:28 +08:00
let out = Promise.defer()
2020-12-17 10:36:48 +08:00
let form, contentType
2023-10-26 19:02:46 +08:00
this.#body = {}
2020-09-16 20:07:28 +08:00
2020-12-17 10:36:48 +08:00
contentType = this.header('content-type') || DEFAULT_FORM_TYPE
2023-10-30 16:41:37 +08:00
form = new Parser(this.#req, { ...this.#opts, uploadDir: tmpdir })
2020-09-16 20:07:28 +08:00
2023-10-26 19:02:46 +08:00
form
.on('field', (name, value) => {
if (name === false) {
this.#body = value
return
2020-09-16 20:07:28 +08:00
}
2023-10-26 19:02:46 +08:00
if (~contentType.indexOf('urlencoded')) {
if (
name.slice(0, 2) === '{"' &&
(name.slice(-2) === '"}' || value.slice(-2) === '"}')
) {
name = name.replace(/\s/g, '+')
2020-09-16 20:07:28 +08:00
2023-10-26 19:02:46 +08:00
if (value.slice(0, 1) === '=') value = '=' + value
2020-09-16 20:07:28 +08:00
2023-10-26 19:02:46 +08:00
return Object.assign(this.#body, JSON.parse(name + value))
}
2020-09-16 20:07:28 +08:00
}
2023-10-26 19:02:46 +08:00
if (name.slice(-2) === '[]') {
name = name.slice(0, -2)
if (typeof value === 'string') {
value = [value]
}
} else if (name.slice(-1) === ']') {
let key = name.slice(name.lastIndexOf('[') + 1, -1)
2020-09-16 20:07:28 +08:00
name = name.slice(0, name.lastIndexOf('['))
2023-10-26 19:02:46 +08:00
//多解析一层对象(也仅支持到这一层)
if (name.slice(-1) === ']') {
let pkey = name.slice(name.lastIndexOf('[') + 1, -1)
name = name.slice(0, name.lastIndexOf('['))
2020-09-16 20:07:28 +08:00
2023-10-26 19:02:46 +08:00
if (!this.#body.hasOwnProperty(name)) {
this.#body[name] = {}
}
2020-09-16 20:07:28 +08:00
2023-10-26 19:02:46 +08:00
if (!this.#body[name].hasOwnProperty(pkey)) {
this.#body[name][pkey] = {}
}
2020-09-16 20:07:28 +08:00
2023-10-26 19:02:46 +08:00
this.#body[name][pkey][key] = value
} else {
if (!this.#body.hasOwnProperty(name)) {
this.#body[name] = {}
}
2020-09-16 20:07:28 +08:00
2023-10-26 19:02:46 +08:00
this.#body[name][key] = value
}
return
2020-09-16 20:07:28 +08:00
}
2023-10-26 19:02:46 +08:00
this.#body[name] = value
})
.on('file', (name, file) => {
2023-10-27 19:16:32 +08:00
if (name === false) {
this.#body = file
2023-10-26 19:02:46 +08:00
} else {
2023-10-27 19:16:32 +08:00
if (name.slice(-2) === '[]') {
name = name.slice(0, -2)
}
if (!this.#body.hasOwnProperty(name)) {
this.#body[name] = file
} else {
if (!Array.isArray(this.#body[name])) {
this.#body[name] = [this.#body[name]]
}
this.#body[name].push(file)
2023-10-26 19:02:46 +08:00
}
}
})
.on('error', out.reject)
2023-10-27 19:16:32 +08:00
.on('end', _ => {
if (contentType.includes('urlencoded')) {
2023-10-26 19:02:46 +08:00
for (let i in this.#body) {
if (typeof this.#body[i] === 'string') {
if (!this.#body[i]) {
continue
}
this.#body[i] = Number.parse(this.#body[i])
2020-09-16 20:07:28 +08:00
}
}
}
2023-10-26 19:02:46 +08:00
out.resolve(this.#body)
})
2020-09-16 20:07:28 +08:00
return out.promise
}
//获取响应头
header(key = '') {
key = key ? (key + '').toLowerCase() : null
2023-10-26 19:02:46 +08:00
return !!key ? this.#req.headers[key] : this.#req.headers
2020-09-16 20:07:28 +08:00
}
2020-09-21 17:57:42 +08:00
// 读取cookie
cookie(key) {
if (key) {
2023-10-26 19:02:46 +08:00
return this.#cookies[key]
}
return this.#cookies
}
get query() {
if (!this.#query) {
let para = parse(this.#req.url).query
2023-10-30 17:04:07 +08:00
this.#query = QS.parse(para)
2023-10-26 19:02:46 +08:00
}
return this.#query
}
get body() {
if (this.#body) {
return this.#body
2020-09-21 17:57:42 +08:00
}
2023-10-26 19:02:46 +08:00
return this.#parseBody()
}
get cookies() {
return this.#cookies
}
get headers() {
return this.#req.headers
2020-09-21 17:57:42 +08:00
}
2020-09-16 20:07:28 +08:00
//获取客户端IP
2023-10-26 19:02:46 +08:00
get ip() {
2020-09-16 20:07:28 +08:00
return (
2023-10-26 19:02:46 +08:00
this.headers['x-real-ip'] ||
this.headers['x-forwarded-for'] ||
this.#req.connection.remoteAddress.replace('::ffff:', '')
2020-09-16 20:07:28 +08:00
)
}
}