From 8662e7f93da5e87534379e5f2d8d96f493057d1e Mon Sep 17 00:00:00 2001 From: Jordan Tucker Date: Fri, 21 Jun 2024 18:02:32 -0500 Subject: [PATCH] fix: preserve multiple set-cookie headers --- src/controllers/historyController.ts | 10 ++------- src/utils/misc.ts | 26 +++++++++++++++++++++++ src/views/httpResponseTextDocumentView.ts | 15 +++---------- src/views/httpResponseWebview.ts | 23 ++++---------------- 4 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/controllers/historyController.ts b/src/controllers/historyController.ts index 248c9b17..ca24003b 100644 --- a/src/controllers/historyController.ts +++ b/src/controllers/historyController.ts @@ -6,6 +6,7 @@ import * as path from 'path'; import { QuickPickItem, window, workspace } from 'vscode'; import { HistoricalHttpRequest } from '../models/httpRequest'; import { trace } from "../utils/decorator"; +import { formatHeaders } from '../utils/misc'; import { UserDataManager } from '../utils/userDataManager'; dayjs.extend(relativeTime); @@ -62,14 +63,7 @@ export class HistoryController { private async createRequestInTempFile(request: HistoricalHttpRequest): Promise { const file = await this.createTempFile(); let output = `${request.method.toUpperCase()} ${request.url}${EOL}`; - if (request.headers) { - for (const header in request.headers) { - if (request.headers.hasOwnProperty(header)) { - const value = request.headers[header]; - output += `${header}: ${value}${EOL}`; - } - } - } + output += formatHeaders(request.headers); if (request.body) { output += `${EOL}${request.body}`; } diff --git a/src/utils/misc.ts b/src/utils/misc.ts index fd4e8297..3f583da3 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -33,6 +33,32 @@ export function removeHeader(headers: RequestHeaders | ResponseHeaders, name: st } } +export function formatHeaders(headers: RequestHeaders | ResponseHeaders): string { + let headerString = ''; + for (const header in headers) { + if (headers.hasOwnProperty(header)) { + let value = headers[header]; + // Handle set-cookie as a special case since multiple entries + // should appear as their own header. For example: + // set-cookie: a=b + // set-cookie: c=d + // Not: + // set-cookie: a=b,c=d + if (header.toLowerCase() === 'set-cookie') { + if (typeof value === 'string') { + value = [value]; + } + for (const cookie of >value) { + headerString += `${header}: ${cookie}\n`; + } + } else { + headerString += `${header}: ${value}\n`; + } + } + } + return headerString; +} + export function md5(text: string | Buffer): string { return crypto.createHash('md5').update(text).digest('hex'); } diff --git a/src/views/httpResponseTextDocumentView.ts b/src/views/httpResponseTextDocumentView.ts index 2da19997..803c83a8 100644 --- a/src/views/httpResponseTextDocumentView.ts +++ b/src/views/httpResponseTextDocumentView.ts @@ -1,10 +1,10 @@ import { EOL } from 'os'; import { languages, Position, Range, TextDocument, ViewColumn, window, workspace } from 'vscode'; -import { RequestHeaders, ResponseHeaders } from '../models/base'; import { SystemSettings } from '../models/configurationSettings'; import { HttpResponse } from '../models/httpResponse'; import { PreviewOption } from '../models/previewOption'; import { MimeUtility } from '../utils/mimeUtility'; +import { formatHeaders } from '../utils/misc'; import { ResponseFormatUtility } from '../utils/responseFormatUtility'; export class HttpResponseTextDocumentView { @@ -49,7 +49,7 @@ export class HttpResponseTextDocumentView { // for add request details const request = response.request; content += `${request.method} ${request.url} HTTP/1.1${EOL}`; - content += this.formatHeaders(request.headers); + content += formatHeaders(request.headers); if (request.body) { if (typeof request.body !== 'string') { request.body = 'NOTE: Request Body From Is File Not Shown'; @@ -62,7 +62,7 @@ export class HttpResponseTextDocumentView { if (previewOption !== PreviewOption.Body) { content += `HTTP/${response.httpVersion} ${response.statusCode} ${response.statusMessage}${EOL}`; - content += this.formatHeaders(response.headers); + content += formatHeaders(response.headers); } if (previewOption !== PreviewOption.Headers) { @@ -73,15 +73,6 @@ export class HttpResponseTextDocumentView { return content; } - private formatHeaders(headers: RequestHeaders | ResponseHeaders): string { - let headerString = ''; - for (const header in headers) { - const value = headers[header] as string; - headerString += `${header}: ${value}${EOL}`; - } - return headerString; - } - private getVSCodeDocumentLanguageId(response: HttpResponse) { if (this.settings.previewOption === PreviewOption.Body) { const contentType = response.contentType; diff --git a/src/views/httpResponseWebview.ts b/src/views/httpResponseWebview.ts index 6dbc2148..65b81356 100644 --- a/src/views/httpResponseWebview.ts +++ b/src/views/httpResponseWebview.ts @@ -1,7 +1,6 @@ import * as fs from 'fs-extra'; import * as os from 'os'; import { Clipboard, commands, env, ExtensionContext, Uri, ViewColumn, WebviewPanel, window, workspace } from 'vscode'; -import { RequestHeaders, ResponseHeaders } from '../models/base'; import { SystemSettings } from '../models/configurationSettings'; import { HttpRequest } from '../models/httpRequest'; import { HttpResponse } from '../models/httpResponse'; @@ -9,7 +8,7 @@ import { PreviewOption } from '../models/previewOption'; import { trace } from '../utils/decorator'; import { disposeAll } from '../utils/dispose'; import { MimeUtility } from '../utils/mimeUtility'; -import { base64, getHeader, isJSONString } from '../utils/misc'; +import { base64, formatHeaders, getHeader, isJSONString } from '../utils/misc'; import { ResponseFormatUtility } from '../utils/responseFormatUtility'; import { UserDataManager } from '../utils/userDataManager'; import { BaseWebview } from './baseWebview'; @@ -213,7 +212,7 @@ export class HttpResponseWebview extends BaseWebview { private getFullResponseString(response: HttpResponse): string { const statusLine = `HTTP/${response.httpVersion} ${response.statusCode} ${response.statusMessage}${os.EOL}`; - const headerString = Object.entries(response.headers).reduce((acc, [name, value]) => acc + `${name}: ${value}${os.EOL}`, ''); + const headerString = formatHeaders(response.headers); const body = response.body ? `${os.EOL}${response.body}` : ''; return `${statusLine}${headerString}${body}`; } @@ -284,7 +283,7 @@ export class HttpResponseWebview extends BaseWebview { // for add request details const request = response.request; const requestNonBodyPart = `${request.method} ${request.url} HTTP/1.1 -${HttpResponseWebview.formatHeaders(request.headers)}`; +${formatHeaders(request.headers)}`; code += hljs.highlight('http', requestNonBodyPart + '\r\n').value; if (request.body) { if (typeof request.body !== 'string') { @@ -305,7 +304,7 @@ ${HttpResponseWebview.formatHeaders(request.headers)}`; if (previewOption !== PreviewOption.Body) { const responseNonBodyPart = `HTTP/${response.httpVersion} ${response.statusCode} ${response.statusMessage} -${HttpResponseWebview.formatHeaders(response.headers)}`; +${formatHeaders(response.headers)}`; code += hljs.highlight('http', responseNonBodyPart + (previewOption !== PreviewOption.Headers ? '\r\n' : '')).value; } @@ -448,20 +447,6 @@ ${HttpResponseWebview.formatHeaders(response.headers)}`; return result; } - private static formatHeaders(headers: RequestHeaders | ResponseHeaders): string { - let headerString = ''; - for (const header in headers) { - if (headers.hasOwnProperty(header)) { - let value = headers[header]; - if (typeof headers[header] !== 'string') { - value = headers[header]; - } - headerString += `${header}: ${value}\n`; - } - } - return headerString; - } - private static getHighlightLanguageAlias(contentType: string | undefined, content: string | null = null): string | null { if (MimeUtility.isJSON(contentType)) { return 'json';