@ekhein/http-request 1.0.21 → 1.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -4
- package/src/agent/index.ts +11 -0
- package/src/exceptions/attempt.exception.ts +13 -0
- package/src/exceptions/index.ts +3 -0
- package/src/exceptions/request.exception.ts +2 -0
- package/src/headers/index.ts +2 -0
- package/src/headers/request.headers.ts +2 -0
- package/src/http-request.ts +228 -0
- package/src/index.ts +5 -0
- package/src/libs/form-data.ts +2 -0
- package/src/libs/index.ts +2 -0
- package/src/proxy/index.ts +3 -0
- package/src/proxy/liuguan.proxy.ts +21780 -0
- package/src/proxy/phone4g.proxy.ts +17 -0
- package/tsconfig.json +24 -0
- package/types/axios.d.ts +5 -0
- package/types/http-request.d.ts +48 -0
- package/types/index.d.ts +1 -0
package/package.json
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekhein/http-request",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "ekhein",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./types/index.d.ts",
|
|
8
|
-
"files": [
|
|
9
|
-
"dist"
|
|
10
|
-
],
|
|
11
8
|
"scripts": {
|
|
12
9
|
"build": "tsc"
|
|
13
10
|
},
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CookieAgentOptions, createCookieAgent, HttpCookieAgent, HttpsCookieAgent } from 'http-cookie-agent/http';
|
|
2
|
+
import { LiuGuanProxy, Phone4GProxy } from '../proxy'
|
|
3
|
+
|
|
4
|
+
export const HttpCookieWithPhone4GProxyAgent = createCookieAgent<Phone4GProxy, [ConstructorParameters<typeof Phone4GProxy>[0], CookieAgentOptions]>(Phone4GProxy)
|
|
5
|
+
export const HttpsCookieWithPhone4GProxyAgent = createCookieAgent<Phone4GProxy, [ConstructorParameters<typeof Phone4GProxy>[0], CookieAgentOptions]>(Phone4GProxy)
|
|
6
|
+
export const HttpCookieWithLiuGuanProxyAgent = createCookieAgent<LiuGuanProxy, [ConstructorParameters<typeof LiuGuanProxy>[0], CookieAgentOptions]>(LiuGuanProxy)
|
|
7
|
+
export const HttpsCookieWithLiuGuanProxyAgent = createCookieAgent<LiuGuanProxy, [ConstructorParameters<typeof LiuGuanProxy>[0], CookieAgentOptions]>(LiuGuanProxy)
|
|
8
|
+
export {
|
|
9
|
+
HttpCookieAgent,
|
|
10
|
+
HttpsCookieAgent
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
|
2
|
+
import { HttpRequestException } from './request.exception';
|
|
3
|
+
|
|
4
|
+
export class HttpRequestAttemptException extends HttpRequestException {
|
|
5
|
+
constructor(
|
|
6
|
+
message: string,
|
|
7
|
+
config?: InternalAxiosRequestConfig,
|
|
8
|
+
request?: any,
|
|
9
|
+
response?: AxiosResponse
|
|
10
|
+
) {
|
|
11
|
+
super(message, "ERR_REQ_ATTEMPT", config, request, response)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from 'axios';
|
|
2
|
+
import tldts from 'tldts';
|
|
3
|
+
import * as cookie from 'cookie';
|
|
4
|
+
import { JSDOM } from 'jsdom';
|
|
5
|
+
import { Cookie, CookieJar } from 'tough-cookie';
|
|
6
|
+
import { ServiceUnavailableException } from '@nestjs/common';
|
|
7
|
+
import { Attempt } from '@ekhein/async-retry-decorator';
|
|
8
|
+
import { ErrorHttpStatusCode, HttpErrorByCode } from '@nestjs/common/utils/http-error-by-code.util';
|
|
9
|
+
import { FormData } from './libs';
|
|
10
|
+
import { HttpRequestHeaders } from './headers';
|
|
11
|
+
import { HttpRequestException, HttpRequestAttemptException } from './exceptions';
|
|
12
|
+
import { HttpCookieAgent, HttpsCookieAgent } from './agent';
|
|
13
|
+
import { HttpCookieWithPhone4GProxyAgent, HttpsCookieWithPhone4GProxyAgent } from './agent';
|
|
14
|
+
import { HttpCookieWithLiuGuanProxyAgent, HttpsCookieWithLiuGuanProxyAgent } from './agent';
|
|
15
|
+
import { HttpRequestConfig } from '../types';
|
|
16
|
+
|
|
17
|
+
export class HttpRequest {
|
|
18
|
+
|
|
19
|
+
public readonly instance: AxiosInstance
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
options: HttpRequestConfig = {}
|
|
23
|
+
) {
|
|
24
|
+
/* Axios */
|
|
25
|
+
this.instance = axios.create({
|
|
26
|
+
...Object.assign({
|
|
27
|
+
["timeout"]: 30e3,
|
|
28
|
+
["headers"]: {},
|
|
29
|
+
["proxy"]: false,
|
|
30
|
+
}, options)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
/* Agent */
|
|
34
|
+
this.instance.interceptors.request.use(
|
|
35
|
+
async request => {
|
|
36
|
+
const jar = Object(request.jar)
|
|
37
|
+
const cookies = Object({jar})
|
|
38
|
+
if (request.useProxy) {
|
|
39
|
+
if (typeof request.useProxy === "boolean") {
|
|
40
|
+
request.httpAgent = new HttpCookieWithLiuGuanProxyAgent({}, {cookies})
|
|
41
|
+
request.httpsAgent = new HttpsCookieWithLiuGuanProxyAgent({}, {cookies})
|
|
42
|
+
}
|
|
43
|
+
if (typeof request.useProxy === "object") {
|
|
44
|
+
if ("clientIp" in request.useProxy) {
|
|
45
|
+
request.httpAgent = new HttpCookieWithPhone4GProxyAgent(request.useProxy, {cookies})
|
|
46
|
+
request.httpsAgent = new HttpsCookieWithPhone4GProxyAgent(request.useProxy, {cookies})
|
|
47
|
+
} else {
|
|
48
|
+
request.httpAgent = new HttpCookieWithLiuGuanProxyAgent(request.useProxy, {cookies})
|
|
49
|
+
request.httpsAgent = new HttpsCookieWithLiuGuanProxyAgent(request.useProxy, {cookies})
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
request.httpAgent = new HttpCookieAgent({cookies})
|
|
54
|
+
request.httpsAgent = new HttpsCookieAgent({cookies})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return request
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
/* Set Cookie */
|
|
61
|
+
this.instance.interceptors.request.use(
|
|
62
|
+
async request => {
|
|
63
|
+
if (request.headers.cookie) {
|
|
64
|
+
const api = new URL(request.url!, request.baseURL)
|
|
65
|
+
const parsed = tldts.parse(api.href)
|
|
66
|
+
const cookies = cookie.parse(request.headers.cookie)
|
|
67
|
+
const domain1 = String(parsed.domain ?? parsed.hostname) /* 顶级域名 */
|
|
68
|
+
const website = Array(api.protocol, domain1).join("//")
|
|
69
|
+
const entries = Object.entries(cookies)
|
|
70
|
+
for (const [key, value] of entries) {
|
|
71
|
+
const cookie = new Cookie({...parsed, key, value})
|
|
72
|
+
await request.jar!.setCookie(cookie, website)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return request
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
/* Cookie Jar */
|
|
80
|
+
this.instance.interceptors.request.use(
|
|
81
|
+
async request => {
|
|
82
|
+
if (!request.jar)
|
|
83
|
+
request.jar = new CookieJar()
|
|
84
|
+
return request
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
/* Headers */
|
|
88
|
+
this.instance.interceptors.request.use(
|
|
89
|
+
async request => {
|
|
90
|
+
if (request.headers) {
|
|
91
|
+
const headers = Object.entries(request.headers)
|
|
92
|
+
for (const [key, value] of headers) {
|
|
93
|
+
if (/^[a-zA-Z]+(?:[A-Z][a-z]*)*$/.test(key)) {
|
|
94
|
+
const lowerKey = String(key).replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
|
|
95
|
+
request.headers.delete(key)
|
|
96
|
+
request.headers.set(lowerKey, value)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return request
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
/* Body */
|
|
105
|
+
this.instance.interceptors.request.use(
|
|
106
|
+
async request => {
|
|
107
|
+
if (request.body) {
|
|
108
|
+
request.headers.setContentType("application/json")
|
|
109
|
+
request.data = request.body
|
|
110
|
+
}
|
|
111
|
+
if (request.form) {
|
|
112
|
+
if (request.form instanceof FormData) {
|
|
113
|
+
request.data = request.form
|
|
114
|
+
} else {
|
|
115
|
+
request.headers.setContentType("application/x-www-form-urlencoded")
|
|
116
|
+
request.data = request.form
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return request
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
/* BaseURL */
|
|
124
|
+
this.instance.interceptors.request.use(
|
|
125
|
+
async request => {
|
|
126
|
+
if (request.baseURL) return request
|
|
127
|
+
if (request.serviceType == 1)
|
|
128
|
+
request.baseURL = process.env.CORE_URL
|
|
129
|
+
if (request.serviceType == 2)
|
|
130
|
+
request.baseURL = process.env.SEKIRO_URL
|
|
131
|
+
return request
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
/* Transform */
|
|
135
|
+
this.instance.interceptors.request.use(
|
|
136
|
+
async request => {
|
|
137
|
+
if (request.serviceType == 1) {}
|
|
138
|
+
if (request.serviceType == 2) {
|
|
139
|
+
const group = String(request.path).split("/").at(-2)
|
|
140
|
+
const action = String(request.path).split("/").at(-1)
|
|
141
|
+
const invoke_timeout = Number(request.timeout! - 5e3)
|
|
142
|
+
request.url = String("/business-demo/invoke")
|
|
143
|
+
request.params = Object({group, action, invoke_timeout})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return request
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
/* Request Hook */
|
|
150
|
+
this.instance.interceptors.request.use(
|
|
151
|
+
options.interceptorHooks?.requestInterceptor,
|
|
152
|
+
options.interceptorHooks?.requestInterceptorCatch
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
/* Response Hook */
|
|
156
|
+
this.instance.interceptors.response.use(
|
|
157
|
+
options.interceptorHooks?.respondInterceptor,
|
|
158
|
+
options.interceptorHooks?.respondInterceptorCatch,
|
|
159
|
+
)
|
|
160
|
+
/* Transform */
|
|
161
|
+
this.instance.interceptors.response.use(
|
|
162
|
+
async response => {
|
|
163
|
+
if (typeof response.headers == "object") {
|
|
164
|
+
if (response.headers instanceof HttpRequestHeaders) {
|
|
165
|
+
if (response.config.serviceType == 1) {
|
|
166
|
+
if (HttpErrorByCode[response.data.err as ErrorHttpStatusCode])
|
|
167
|
+
throw new HttpErrorByCode[response.data.err as ErrorHttpStatusCode](response.data.msg)
|
|
168
|
+
return response.data.res
|
|
169
|
+
}
|
|
170
|
+
if (response.config.serviceType == 2) {
|
|
171
|
+
if (response.data.status)
|
|
172
|
+
throw new ServiceUnavailableException(response.data.message)
|
|
173
|
+
return response.data.data
|
|
174
|
+
}
|
|
175
|
+
if (response.config.html) {
|
|
176
|
+
return new JSDOM(response.data)
|
|
177
|
+
}
|
|
178
|
+
if (response.config.resolveBodyOnly != false) {
|
|
179
|
+
return response.data
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return response
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@Attempt({
|
|
190
|
+
retries: 2,
|
|
191
|
+
ignore: [Error],
|
|
192
|
+
onError(error) {
|
|
193
|
+
if (error instanceof HttpRequestAttemptException) throw error
|
|
194
|
+
if (error instanceof HttpRequestException) {
|
|
195
|
+
switch(error.code) {
|
|
196
|
+
case HttpRequestException.ETIMEDOUT:
|
|
197
|
+
case HttpRequestException.ERR_NETWORK:
|
|
198
|
+
case HttpRequestException.ECONNABORTED: throw error
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
})
|
|
203
|
+
public async execute<R = any>(
|
|
204
|
+
options: HttpRequestConfig
|
|
205
|
+
) {
|
|
206
|
+
return this.instance
|
|
207
|
+
.request<never, R>(options)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public async post<R = any>(
|
|
211
|
+
url: string,
|
|
212
|
+
data?: any,
|
|
213
|
+
opts: HttpRequestConfig = {}
|
|
214
|
+
) {
|
|
215
|
+
return this.instance
|
|
216
|
+
.request<never, R>({...opts, method: "POST", serviceType: 1, data, url})
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
public async invoke<R = any>(
|
|
220
|
+
path: string,
|
|
221
|
+
data: object,
|
|
222
|
+
opts: HttpRequestConfig = {}
|
|
223
|
+
) {
|
|
224
|
+
return this.instance
|
|
225
|
+
.request<never, R>({...opts, method: "POST", serviceType: 2, data, path})
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
}
|
package/src/index.ts
ADDED