]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - shared/extra-utils/requests/requests.ts
Fix silent 500 after resumable upload
[github/Chocobozzz/PeerTube.git] / shared / extra-utils / requests / requests.ts
CommitLineData
c0e8b12e 1/* eslint-disable @typescript-eslint/no-floating-promises */
a1587156 2
9107d791 3import { decode } from 'querystring'
41fb13c3 4import request from 'supertest'
2d1ad5b9 5import { URL } from 'url'
c0e8b12e
C
6import { HttpStatusCode } from '@shared/models'
7import { buildAbsoluteFixturePath } from '../miscs/tests'
09209296 8
c0e8b12e 9export type CommonRequestParams = {
a1587156
C
10 url: string
11 path?: string
a1587156 12 contentType?: string
4c280004 13 range?: string
9107d791 14 redirects?: number
012580d9 15 accept?: string
41d1d075 16 host?: string
c0e8b12e
C
17 token?: string
18 headers?: { [ name: string ]: string }
19 type?: string
20 xForwardedFor?: string
21 expectedStatus?: HttpStatusCode
22}
eec63bbc 23
c0e8b12e
C
24function makeRawRequest (url: string, expectedStatus?: HttpStatusCode, range?: string) {
25 const { host, protocol, pathname } = new URL(url)
eec63bbc 26
c0e8b12e 27 return makeGetRequest({ url: `${protocol}//${host}`, path: pathname, expectedStatus, range })
eec63bbc
C
28}
29
c0e8b12e
C
30function makeGetRequest (options: CommonRequestParams & {
31 query?: any
fb72d2e1 32 rawQuery?: string
eec63bbc 33}) {
c0e8b12e 34 const req = request(options.url).get(options.path)
fb72d2e1
C
35
36 if (options.query) req.query(options.query)
37 if (options.rawQuery) req.query(options.rawQuery)
eec63bbc 38
c0e8b12e
C
39 return buildRequest(req, { contentType: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
40}
eec63bbc 41
c0e8b12e
C
42function makeHTMLRequest (url: string, path: string) {
43 return makeGetRequest({
44 url,
45 path,
46 accept: 'text/html',
47 expectedStatus: HttpStatusCode.OK_200
48 })
49}
eec63bbc 50
c0e8b12e
C
51function makeActivityPubGetRequest (url: string, path: string, expectedStatus = HttpStatusCode.OK_200) {
52 return makeGetRequest({
53 url,
54 path,
55 expectedStatus: expectedStatus,
56 accept: 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8'
57 })
0e1dc3e7
C
58}
59
790c2837
C
60function makeDeleteRequest (options: CommonRequestParams & {
61 query?: any
62 rawQuery?: string
63}) {
c0e8b12e
C
64 const req = request(options.url).delete(options.path)
65
790c2837
C
66 if (options.query) req.query(options.query)
67 if (options.rawQuery) req.query(options.rawQuery)
68
c0e8b12e
C
69 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
70}
71
72function makeUploadRequest (options: CommonRequestParams & {
a1587156 73 method?: 'POST' | 'PUT'
d23dd9fb 74
a1587156 75 fields: { [ fieldName: string ]: any }
77e9f859 76 attaches?: { [ attachName: string ]: any | any[] }
0e1dc3e7 77}) {
c0e8b12e
C
78 let req = options.method === 'PUT'
79 ? request(options.url).put(options.path)
80 : request(options.url).post(options.path)
0e1dc3e7 81
c0e8b12e 82 req = buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
ac81d1a0 83
c0e8b12e 84 buildFields(req, options.fields)
0e1dc3e7 85
77e9f859 86 Object.keys(options.attaches || {}).forEach(attach => {
0e1dc3e7 87 const value = options.attaches[attach]
c0e8b12e 88
2769e297
C
89 if (Array.isArray(value)) {
90 req.attach(attach, buildAbsoluteFixturePath(value[0]), value[1])
91 } else {
92 req.attach(attach, buildAbsoluteFixturePath(value))
93 }
0e1dc3e7
C
94 })
95
d23dd9fb 96 return req
0e1dc3e7
C
97}
98
c0e8b12e 99function makePostBodyRequest (options: CommonRequestParams & {
a1587156 100 fields?: { [ fieldName: string ]: any }
0e1dc3e7 101}) {
c0e8b12e
C
102 const req = request(options.url).post(options.path)
103 .send(options.fields)
0e1dc3e7 104
c0e8b12e 105 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
0e1dc3e7
C
106}
107
108function makePutBodyRequest (options: {
a1587156
C
109 url: string
110 path: string
111 token?: string
112 fields: { [ fieldName: string ]: any }
c0e8b12e 113 expectedStatus?: HttpStatusCode
4bbfc6c6 114}) {
c0e8b12e
C
115 const req = request(options.url).put(options.path)
116 .send(options.fields)
4bbfc6c6 117
c0e8b12e 118 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
4bbfc6c6
C
119}
120
9107d791
C
121function decodeQueryString (path: string) {
122 return decode(path.split('?')[1])
123}
124
c1bc8ee4 125function unwrapBody <T> (test: request.Test): Promise<T> {
e8bd7ce7
C
126 return test.then(res => res.body)
127}
128
c1bc8ee4
C
129function unwrapText (test: request.Test): Promise<string> {
130 return test.then(res => res.text)
131}
132
0305db28
JB
133function unwrapBodyOrDecodeToJSON <T> (test: request.Test): Promise<T> {
134 return test.then(res => {
135 if (res.body instanceof Buffer) {
136 return JSON.parse(new TextDecoder().decode(res.body))
137 }
138
139 return res.body
140 })
141}
142
143function unwrapTextOrDecode (test: request.Test): Promise<string> {
144 return test.then(res => res.text || new TextDecoder().decode(res.body))
145}
146
0e1dc3e7
C
147// ---------------------------------------------------------------------------
148
149export {
e032aec9 150 makeHTMLRequest,
0e1dc3e7 151 makeGetRequest,
9107d791 152 decodeQueryString,
ac81d1a0 153 makeUploadRequest,
0e1dc3e7 154 makePostBodyRequest,
eec63bbc 155 makePutBodyRequest,
4bbfc6c6 156 makeDeleteRequest,
09209296 157 makeRawRequest,
2d1ad5b9 158 makeActivityPubGetRequest,
c1bc8ee4 159 unwrapBody,
0305db28
JB
160 unwrapTextOrDecode,
161 unwrapBodyOrDecodeToJSON,
c0e8b12e
C
162 unwrapText
163}
164
165// ---------------------------------------------------------------------------
166
167function buildRequest (req: request.Test, options: CommonRequestParams) {
168 if (options.contentType) req.set('Accept', options.contentType)
169 if (options.token) req.set('Authorization', 'Bearer ' + options.token)
170 if (options.range) req.set('Range', options.range)
171 if (options.accept) req.set('Accept', options.accept)
172 if (options.host) req.set('Host', options.host)
173 if (options.redirects) req.redirects(options.redirects)
174 if (options.expectedStatus) req.expect(options.expectedStatus)
175 if (options.xForwardedFor) req.set('X-Forwarded-For', options.xForwardedFor)
176 if (options.type) req.type(options.type)
177
178 Object.keys(options.headers || {}).forEach(name => {
179 req.set(name, options.headers[name])
180 })
181
182 return req
183}
184
185function buildFields (req: request.Test, fields: { [ fieldName: string ]: any }, namespace?: string) {
186 if (!fields) return
187
188 let formKey: string
189
190 for (const key of Object.keys(fields)) {
191 if (namespace) formKey = `${namespace}[${key}]`
192 else formKey = key
193
194 if (fields[key] === undefined) continue
195
196 if (Array.isArray(fields[key]) && fields[key].length === 0) {
63436fc5 197 req.field(key, [])
c0e8b12e
C
198 continue
199 }
200
201 if (fields[key] !== null && typeof fields[key] === 'object') {
202 buildFields(req, fields[key], formKey)
203 } else {
204 req.field(formKey, fields[key])
205 }
206 }
0e1dc3e7 207}