From d085ad57f6f9be026f8de64fd1b5cfd9ee0fb730 Mon Sep 17 00:00:00 2001 From: Hexxa Date: Mon, 20 Nov 2023 10:05:10 +0800 Subject: [PATCH] fix: improve sending request performance [INS-3280, INS-3256] (#6763) * fix: improve sending request performance [INS-3280] * chore: resolve conflict --- .../src/ui/components/request-url-bar.tsx | 34 ++++--- packages/insomnia/src/ui/routes/request.tsx | 93 +++++++++++-------- 2 files changed, 73 insertions(+), 54 deletions(-) diff --git a/packages/insomnia/src/ui/components/request-url-bar.tsx b/packages/insomnia/src/ui/components/request-url-bar.tsx index 9a93f7115..8082e87b4 100644 --- a/packages/insomnia/src/ui/components/request-url-bar.tsx +++ b/packages/insomnia/src/ui/components/request-url-bar.tsx @@ -1,13 +1,10 @@ - import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'; -import { useFetcher, useParams, useRouteLoaderData } from 'react-router-dom'; +import { useFetcher, useParams, useRouteLoaderData, useSearchParams } from 'react-router-dom'; import { useInterval } from 'react-use'; import styled from 'styled-components'; -import { RENDER_PURPOSE_SEND } from '../../common/render'; import * as models from '../../models'; import { isEventStreamRequest } from '../../models/request'; -import { fetchRequestData, tryToInterpolateRequest, tryToTransformRequestWithPlugins } from '../../network/network'; import { tryToInterpolateRequestOrShowRenderErrorModal } from '../../utils/try-interpolate'; import { buildQueryStringFromParams, joinUrlAndQueryString } from '../../utils/url/querystring'; import { SegmentEvent } from '../analytics'; @@ -53,6 +50,25 @@ export const RequestUrlBar = forwardRef(({ setLoading, onPaste, }, ref) => { + const [searchParams, setSearchParams] = useSearchParams(); + if (searchParams.has('error')) { + showAlert({ + title: 'Unexpected Request Failure', + message: ( +
+

The request failed due to an unhandled error:

+ +
{searchParams.get('error')}
+
+
+ ), + }); + + // clean up params + searchParams.delete('error'); + setSearchParams({}); + } + const { activeWorkspace, activeEnvironment, @@ -148,15 +164,7 @@ export const RequestUrlBar = forwardRef(({ } try { - const { request, - environment } = await fetchRequestData(requestId); - - const renderResult = await tryToInterpolateRequest(request, environment._id, RENDER_PURPOSE_SEND); - const renderedRequest = await tryToTransformRequestWithPlugins(renderResult); - renderedRequest && send({ - renderedRequest, - shouldPromptForPathAfterResponse, - }); + send({ requestId, shouldPromptForPathAfterResponse }); } catch (err) { showAlert({ title: 'Unexpected Request Failure', diff --git a/packages/insomnia/src/ui/routes/request.tsx b/packages/insomnia/src/ui/routes/request.tsx index 8abe35e0a..3e8469734 100644 --- a/packages/insomnia/src/ui/routes/request.tsx +++ b/packages/insomnia/src/ui/routes/request.tsx @@ -9,7 +9,7 @@ import { version } from '../../../package.json'; import { CONTENT_TYPE_EVENT_STREAM, CONTENT_TYPE_GRAPHQL, CONTENT_TYPE_JSON, METHOD_GET, METHOD_POST } from '../../common/constants'; import { ChangeBufferEvent, database } from '../../common/database'; import { getContentDispositionHeader } from '../../common/misc'; -import { RENDER_PURPOSE_SEND, RenderedRequest } from '../../common/render'; +import { RENDER_PURPOSE_SEND } from '../../common/render'; import { ResponsePatch } from '../../main/network/libcurl-promise'; import * as models from '../../models'; import { BaseModel } from '../../models'; @@ -23,7 +23,7 @@ import { RequestVersion } from '../../models/request-version'; import { Response } from '../../models/response'; import { isWebSocketRequestId, WebSocketRequest } from '../../models/websocket-request'; import { WebSocketResponse } from '../../models/websocket-response'; -import { fetchRequestData, responseTransform, sendCurlAndWriteTimeline, tryToInterpolateRequest } from '../../network/network'; +import { fetchRequestData, responseTransform, sendCurlAndWriteTimeline, tryToInterpolateRequest, tryToTransformRequestWithPlugins } from '../../network/network'; import { invariant } from '../../utils/invariant'; import { SegmentEvent } from '../analytics'; import { updateMimeType } from '../components/dropdowns/content-type-dropdown'; @@ -317,61 +317,72 @@ const writeToDownloadPath = (downloadPathAndName: string, responsePatch: Respons }; export interface SendActionParams { - renderedRequest: RenderedRequest; + requestId: string; shouldPromptForPathAfterResponse?: boolean; } export const sendAction: ActionFunction = async ({ request, params }) => { const { requestId, workspaceId } = params; invariant(typeof requestId === 'string', 'Request ID is required'); + invariant(workspaceId, 'Workspace ID is required'); + const req = await requestOperations.getById(requestId) as Request; invariant(req, 'Request not found'); - invariant(workspaceId, 'Workspace ID is required'); + const { environment, settings, clientCertificates, caCert, - activeEnvironmentId } = await fetchRequestData(requestId); - const { renderedRequest, shouldPromptForPathAfterResponse } = await request.json() as SendActionParams; - const renderResult = await tryToInterpolateRequest(req, environment._id, RENDER_PURPOSE_SEND); + activeEnvironmentId, + } = await fetchRequestData(requestId); + try { + const { shouldPromptForPathAfterResponse } = await request.json() as SendActionParams; + const renderedResult = await tryToInterpolateRequest(req, environment._id, RENDER_PURPOSE_SEND); + const renderedRequest = await tryToTransformRequestWithPlugins(renderedResult); - const response = await sendCurlAndWriteTimeline( - renderedRequest, - clientCertificates, - caCert, - settings, - ); - const requestMeta = await models.requestMeta.getByParentId(requestId); - invariant(requestMeta, 'RequestMeta not found'); - const responsePatch = await responseTransform(response, activeEnvironmentId, renderedRequest, renderResult.context); - const is2XXWithBodyPath = responsePatch.statusCode && responsePatch.statusCode >= 200 && responsePatch.statusCode < 300 && responsePatch.bodyPath; - const shouldWriteToFile = shouldPromptForPathAfterResponse && is2XXWithBodyPath; - if (!shouldWriteToFile) { - const response = await models.response.create(responsePatch, settings.maxHistoryResponses); - await models.requestMeta.update(requestMeta, { activeResponseId: response._id }); - // setLoading(false); - return null; - } - if (requestMeta.downloadPath) { - const header = getContentDispositionHeader(responsePatch.headers || []); - const name = header - ? contentDisposition.parse(header.value).parameters.filename - : `${req.name.replace(/\s/g, '-').toLowerCase()}.${responsePatch.contentType && mimeExtension(responsePatch.contentType) || 'unknown'}`; - return writeToDownloadPath(path.join(requestMeta.downloadPath, name), responsePatch, requestMeta, settings.maxHistoryResponses); - } else { - const defaultPath = window.localStorage.getItem('insomnia.sendAndDownloadLocation'); - const { filePath } = await window.dialog.showSaveDialog({ - title: 'Select Download Location', - buttonLabel: 'Save', - // NOTE: An error will be thrown if defaultPath is supplied but not a String - ...(defaultPath ? { defaultPath } : {}), - }); - if (!filePath) { + const response = await sendCurlAndWriteTimeline( + renderedRequest, + clientCertificates, + caCert, + settings, + ); + + const requestMeta = await models.requestMeta.getByParentId(requestId); + invariant(requestMeta, 'RequestMeta not found'); + const responsePatch = await responseTransform(response, activeEnvironmentId, renderedRequest, renderedResult.context); + const is2XXWithBodyPath = responsePatch.statusCode && responsePatch.statusCode >= 200 && responsePatch.statusCode < 300 && responsePatch.bodyPath; + const shouldWriteToFile = shouldPromptForPathAfterResponse && is2XXWithBodyPath; + if (!shouldWriteToFile) { + const response = await models.response.create(responsePatch, settings.maxHistoryResponses); + await models.requestMeta.update(requestMeta, { activeResponseId: response._id }); // setLoading(false); return null; } - window.localStorage.setItem('insomnia.sendAndDownloadLocation', filePath); - return writeToDownloadPath(filePath, responsePatch, requestMeta, settings.maxHistoryResponses); + if (requestMeta.downloadPath) { + const header = getContentDispositionHeader(responsePatch.headers || []); + const name = header + ? contentDisposition.parse(header.value).parameters.filename + : `${req.name.replace(/\s/g, '-').toLowerCase()}.${responsePatch.contentType && mimeExtension(responsePatch.contentType) || 'unknown'}`; + return writeToDownloadPath(path.join(requestMeta.downloadPath, name), responsePatch, requestMeta, settings.maxHistoryResponses); + } else { + const defaultPath = window.localStorage.getItem('insomnia.sendAndDownloadLocation'); + const { filePath } = await window.dialog.showSaveDialog({ + title: 'Select Download Location', + buttonLabel: 'Save', + // NOTE: An error will be thrown if defaultPath is supplied but not a String + ...(defaultPath ? { defaultPath } : {}), + }); + if (!filePath) { + // setLoading(false); + return null; + } + window.localStorage.setItem('insomnia.sendAndDownloadLocation', filePath); + return writeToDownloadPath(filePath, responsePatch, requestMeta, settings.maxHistoryResponses); + } + } catch (e) { + const url = new URL(request.url); + url.searchParams.set('error', e); + return redirect(`${url.pathname}?${url.searchParams}`); } }; export const deleteAllResponsesAction: ActionFunction = async ({ params }) => {