mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
eventstream pass 2 (#6170)
* match ssl timeline * dedupe timeline logger * fix lint * fix timeline * send headers
This commit is contained in:
parent
432c4a05c3
commit
00cd22c67b
@ -18,6 +18,7 @@ import { urlMatchesCertHost } from '../../network/url-matches-cert-host';
|
||||
import { invariant } from '../../utils/invariant';
|
||||
import { setDefaultProtocol } from '../../utils/url/protocol';
|
||||
import { createConfiguredCurlInstance } from './libcurl-promise';
|
||||
import { parseHeaderStrings } from './parse-header-strings';
|
||||
|
||||
export interface CurlConnection extends Curl {
|
||||
_id: string;
|
||||
@ -138,7 +139,6 @@ const openCurlConnection = async (
|
||||
const clientCertificates = await models.clientCertificate.findByParentId(options.workspaceId);
|
||||
const filteredClientCertificates = clientCertificates.filter(c => !c.disabled && urlMatchesCertHost(setDefaultProtocol(c.host, 'https:'), options.url));
|
||||
const { curl, debugTimeline } = createConfiguredCurlInstance({
|
||||
// TODO: get cookies here
|
||||
req: { ...request, cookieJar: options.cookieJar, cookies: [] },
|
||||
finalUrl: options.url,
|
||||
settings,
|
||||
@ -146,33 +146,12 @@ const openCurlConnection = async (
|
||||
certificates: filteredClientCertificates,
|
||||
});
|
||||
debugTimeline.forEach(entry => timelineFileStreams.get(options.requestId)?.write(JSON.stringify(entry) + '\n'));
|
||||
curl.enable(CurlFeature.StreamResponse);
|
||||
curl.setOpt(Curl.option.DEBUGFUNCTION, (infoType, buffer) => {
|
||||
const isSSLData = infoType === CurlInfoDebug.SslDataIn || infoType === CurlInfoDebug.SslDataOut;
|
||||
const isEmpty = buffer.length === 0;
|
||||
// Don't show cookie setting because this will display every domain in the jar
|
||||
const isAddCookie = infoType === CurlInfoDebug.Text && buffer.toString('utf8').indexOf('Added cookie') === 0;
|
||||
if (isSSLData || isEmpty || isAddCookie) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: resolves "Text" from CurlInfoDebug[CurlInfoDebug.Text]
|
||||
const name = CurlInfoDebug[infoType] as keyof typeof CurlInfoDebug;
|
||||
let timelineMessage;
|
||||
const isRequestData = infoType === CurlInfoDebug.DataOut;
|
||||
if (isRequestData) {
|
||||
// Ignore large post data messages
|
||||
const isLessThan10KB = buffer.length / 1024 < (settings.maxTimelineDataSizeKB || 1);
|
||||
timelineMessage = isLessThan10KB ? buffer.toString('utf8') : `(${describeByteSize(buffer.length)} hidden)`;
|
||||
}
|
||||
const isResponseData = infoType === CurlInfoDebug.DataIn;
|
||||
if (!isResponseData) {
|
||||
const value = timelineMessage || buffer.toString('utf8');
|
||||
timelineFileStreams.get(options.requestId)?.write(JSON.stringify({ name, value, timestamp: Date.now() }) + '\n');
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
curl.on('error', async (error, errorCode) => {
|
||||
CurlConnections.set(options.requestId, curl);
|
||||
CurlConnections.get(options.requestId)?.enable(CurlFeature.StreamResponse);
|
||||
// TODO: add authHeader and request body?
|
||||
const headerStrings = parseHeaderStrings({ req: request, finalUrl: options.url });
|
||||
CurlConnections.get(options.requestId)?.setOpt(Curl.option.HTTPHEADER, headerStrings);
|
||||
CurlConnections.get(options.requestId)?.on('error', async (error, errorCode) => {
|
||||
const errorEvent: CurlErrorEvent = {
|
||||
_id: uuidV4(),
|
||||
requestId: options.requestId,
|
||||
@ -193,7 +172,35 @@ const openCurlConnection = async (
|
||||
}
|
||||
});
|
||||
|
||||
curl.on('stream', async (stream: Readable, _code: number, [headersWithStatus]: HeaderInfo[]) => {
|
||||
CurlConnections.get(options.requestId)?.setOpt(Curl.option.DEBUGFUNCTION, (infoType, buffer) => {
|
||||
const isSSLData = infoType === CurlInfoDebug.SslDataIn || infoType === CurlInfoDebug.SslDataOut;
|
||||
const isEmpty = buffer.length === 0;
|
||||
// Don't show cookie setting because this will display every domain in the jar
|
||||
const isAddCookie = infoType === CurlInfoDebug.Text && buffer.toString('utf8').indexOf('Added cookie') === 0;
|
||||
if (isSSLData || isEmpty || isAddCookie) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: resolves "Text" from CurlInfoDebug[CurlInfoDebug.Text]
|
||||
let name = CurlInfoDebug[infoType] as keyof typeof CurlInfoDebug;
|
||||
let timelineMessage;
|
||||
const isRequestData = infoType === CurlInfoDebug.DataOut;
|
||||
if (isRequestData) {
|
||||
// Ignore large post data messages
|
||||
const isLessThan10KB = buffer.length / 1024 < (settings.maxTimelineDataSizeKB || 1);
|
||||
timelineMessage = isLessThan10KB ? buffer.toString('utf8') : `(${describeByteSize(buffer.length)} hidden)`;
|
||||
}
|
||||
const isResponseData = infoType === CurlInfoDebug.DataIn;
|
||||
if (isResponseData) {
|
||||
timelineMessage = `Received ${describeByteSize(buffer.length)} chunk`;
|
||||
name = 'Text';
|
||||
}
|
||||
const value = timelineMessage || buffer.toString('utf8');
|
||||
timelineFileStreams.get(options.requestId)?.write(JSON.stringify({ name, value, timestamp: Date.now() }) + '\n');
|
||||
return 0;
|
||||
});
|
||||
|
||||
CurlConnections.get(options.requestId)?.on('stream', async (stream: Readable, _code: number, [headersWithStatus]: HeaderInfo[]) => {
|
||||
event.sender.send(readyStateChannel, true);
|
||||
const { timeline, responseHeaders, statusCode, statusMessage, httpVersion } = parseHeadersAndBuildTimeline(options.url, headersWithStatus);
|
||||
|
||||
@ -233,10 +240,7 @@ const openCurlConnection = async (
|
||||
}
|
||||
timeline.map(t => timelineFileStreams.get(options.requestId)?.write(JSON.stringify(t) + '\n'));
|
||||
|
||||
// we are going to write the response stream to this file
|
||||
const writableStream = eventLogFileStreams.get(request._id);
|
||||
// two ways to go here
|
||||
invariant(writableStream, 'writableStream should be defined');
|
||||
invariant(eventLogFileStreams.get(request._id), 'writableStream should be defined');
|
||||
for await (const chunk of stream) {
|
||||
const messageEvent: CurlMessageEvent = {
|
||||
_id: uuidV4(),
|
||||
@ -254,7 +258,6 @@ const openCurlConnection = async (
|
||||
event.sender.send(readyStateChannel, false);
|
||||
});
|
||||
curl.perform();
|
||||
CurlConnections.set(options.requestId, curl);
|
||||
} catch (e) {
|
||||
console.error('unhandled error:', e);
|
||||
|
||||
|
@ -122,8 +122,8 @@ export const curlRequest = (options: CurlRequestOptions) => new Promise<CurlRequ
|
||||
curl.setOpt(Curl.option.CUSTOMREQUEST, method);
|
||||
}
|
||||
|
||||
const requestBody = parseRequestBody({ body, method });
|
||||
const requestBodyPath = await parseRequestBodyPath(body);
|
||||
const requestBody = parseRequestBody({ body, method });
|
||||
const isMultipart = body.mimeType === CONTENT_TYPE_FORM_DATA && requestBodyPath;
|
||||
let requestFileDescriptor: number | undefined;
|
||||
const { authentication } = req;
|
||||
@ -165,7 +165,7 @@ export const curlRequest = (options: CurlRequestOptions) => new Promise<CurlRequ
|
||||
responseBodyWriteStream.write(buffer);
|
||||
return buffer.length;
|
||||
});
|
||||
// set up response logger
|
||||
|
||||
curl.setOpt(Curl.option.DEBUGFUNCTION, (infoType, buffer) => {
|
||||
const isSSLData = infoType === CurlInfoDebug.SslDataIn || infoType === CurlInfoDebug.SslDataOut;
|
||||
const isEmpty = buffer.length === 0;
|
||||
@ -193,7 +193,6 @@ export const curlRequest = (options: CurlRequestOptions) => new Promise<CurlRequ
|
||||
debugTimeline.push({ name, value, timestamp: Date.now() });
|
||||
return 0;
|
||||
});
|
||||
|
||||
// returns "rawHeaders" string in a buffer, rather than HeaderInfo[] type which is an object with deduped keys
|
||||
// this provides support for multiple set-cookies and duplicated headers
|
||||
curl.enable(CurlFeature.Raw);
|
||||
@ -389,6 +388,7 @@ export const createConfiguredCurlInstance = ({
|
||||
if (authentication.type === AUTH_NETRC) {
|
||||
curl.setOpt(Curl.option.NETRC, CurlNetrc.Required);
|
||||
}
|
||||
|
||||
return { curl, debugTimeline };
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user