Skip to content

Commit

Permalink
Add preview option to display different parts of response, even inclu…
Browse files Browse the repository at this point in the history
…ding request #99
  • Loading branch information
Huachao committed Sep 22, 2017
1 parent 0d9a69e commit c46e7c1
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 16 deletions.
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,17 @@
"type": "boolean",
"default": false,
"description": "Suppress response body content type validation"
},
"rest-client.previewOption": {
"type": "string",
"enum": [
"full",
"headers",
"body",
"exchange"
],
"default": "full",
"description": "Response preview output option. 'full' for whole response message(status line, headers and body). 'headers' for response headers(as well as status line). 'body' for response body only. 'exchange' for whole HTTP exchange (request and response)"
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/httpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ export class HttpClient {
response.timingPhases.tcp,
response.timingPhases.firstByte,
response.timingPhases.download
),
new HttpRequest(
options.method,
options.url,
HttpClient.capitalizeHeaderName(response.toJSON().request.headers),
httpRequest.body,
httpRequest.rawBody
)));
})
.on('data', function (data) {
Expand Down Expand Up @@ -272,4 +279,16 @@ export class HttpClient {
return;
}
}

private static capitalizeHeaderName(headers: { [key: string]: string }): { [key: string]: string } {
let normalizedHeaders = {};
if (headers) {
for (let header in headers) {
let capitalizedName = header.replace(/([^-]+)/g, h => h.charAt(0).toUpperCase() + h.slice(1));
normalizedHeaders[capitalizedName] = headers[header];
}
}

return normalizedHeaders;
}
}
6 changes: 6 additions & 0 deletions src/models/configurationSettings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { workspace } from 'vscode';
import { HostCertificate } from '../models/hostCertificate';
import { PreviewOption, fromString as ParsePreviewOptionStr } from '../models/previewOption';

export interface IRestClientSettings {
followRedirect: boolean;
Expand All @@ -22,6 +23,8 @@ export interface IRestClientSettings {
hostCertificates: Map<string, HostCertificate>;
useTrunkedTransferEncodingForSendingFileContent: boolean;
suppressResponseBodyContentTypeValidationWarning: boolean;

previewOption: PreviewOption;
}

export class RestClientSettings implements IRestClientSettings {
Expand All @@ -46,6 +49,8 @@ export class RestClientSettings implements IRestClientSettings {
public useTrunkedTransferEncodingForSendingFileContent: boolean;
public suppressResponseBodyContentTypeValidationWarning: boolean;

public previewOption: PreviewOption;

public constructor() {
workspace.onDidChangeConfiguration(() => {
this.initializeSettings();
Expand Down Expand Up @@ -78,6 +83,7 @@ export class RestClientSettings implements IRestClientSettings {
this.hostCertificates = restClientSettings.get<Map<string, HostCertificate>>("certificates", new Map<string, HostCertificate>());
this.useTrunkedTransferEncodingForSendingFileContent = restClientSettings.get<boolean>("useTrunkedTransferEncodingForSendingFileContent", true);
this.suppressResponseBodyContentTypeValidationWarning = restClientSettings.get<boolean>("suppressResponseBodyContentTypeValidationWarning", false);
this.previewOption = ParsePreviewOptionStr(restClientSettings.get<string>("previewOption", "full"));

let httpSettings = workspace.getConfiguration('http');
this.proxy = httpSettings.get<string>('proxy', undefined);
Expand Down
26 changes: 24 additions & 2 deletions src/models/httpRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,34 @@
import { Stream } from 'stream';

export class HttpRequest {
public constructor(public method: string, public url: string, public headers: { [key: string]: string }, public body: string | Stream, public rawBody: string) {
public constructor(
public method: string,
public url: string,
public headers: { [key: string]: string },
public body: string | Stream,
public rawBody: string) {
}

public getRequestHeaderValue(name: string) {
if (this.headers) {
for (let header in this.headers) {
if (header.toLowerCase() === name.toLowerCase()) {
return this.headers[header];
}
}
}

return null;
}
}

export class SerializedHttpRequest {
public constructor(public method: string, public url: string, public headers: { [key: string]: string }, public body: string, public startTime: number) {
public constructor(
public method: string,
public url: string,
public headers: { [key: string]: string },
public body: string,
public startTime: number) {
}

public static convertFromHttpRequest(httpRequest: HttpRequest, startTime: number = Date.now()): SerializedHttpRequest {
Expand Down
4 changes: 3 additions & 1 deletion src/models/httpResponse.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";

import { HttpRequest } from "./httpRequest";
import { HttpResponseTimingPhases } from './httpResponseTimingPhases';

export class HttpResponse {
Expand All @@ -14,7 +15,8 @@ export class HttpResponse {
public bodySizeInBytes: number,
public headersSizeInBytes: number,
public bodyStream: Buffer,
public timingPhases: HttpResponseTimingPhases) {
public timingPhases: HttpResponseTimingPhases,
public request: HttpRequest) {
}

public getResponseHeaderValue(name: string) {
Expand Down
24 changes: 24 additions & 0 deletions src/models/previewOption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use strict";

export enum PreviewOption {
Full,
Headers,
Body,
Exchange
}

export function fromString(value: string): PreviewOption {
value = value.toLowerCase();
switch (value) {
case 'full':
return PreviewOption.Full;
case 'headers':
return PreviewOption.Headers;
case 'body':
return PreviewOption.Body;
case 'exchange':
return PreviewOption.Exchange;
default:
return PreviewOption.Full;
}
}
57 changes: 44 additions & 13 deletions src/views/httpResponseTextDocumentContentProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { HttpResponse } from "../models/httpResponse";
import { MimeUtility } from '../mimeUtility';
import { ResponseFormatUtility } from '../responseFormatUtility';
import { ResponseStore } from '../responseStore';
import { PreviewOption } from '../models/previewOption';
import * as Constants from '../constants';
import * as path from 'path';

const autoLinker = require('autolinker');
const hljs = require('highlight.js');

let autoLinker = require('autolinker');

export class HttpResponseTextDocumentContentProvider extends BaseTextDocumentContentProvider {
private static cssFilePath: string = path.join(extensions.getExtension(Constants.ExtensionId).extensionPath, Constants.CSSFolderName, Constants.CSSFileName);

Expand All @@ -34,7 +34,7 @@ export class HttpResponseTextDocumentContentProvider extends BaseTextDocumentCon
if (contentType && MimeUtility.isBrowserSupportedImageFormat(contentType)) {
innerHtml = `<img src="data:${contentType};base64,${new Buffer(response.bodyStream).toString('base64')}">`;
} else {
let code = this.highlightResponse(response, this.settings.suppressResponseBodyContentTypeValidationWarning);
let code = this.highlightResponse(response);
width = (code.split(/\r\n|\r|\n/).length + 1).toString().length;
innerHtml = `<pre><code>${this.addLineNums(code)}</code></pre>`;
}
Expand All @@ -53,19 +53,50 @@ export class HttpResponseTextDocumentContentProvider extends BaseTextDocumentCon
}
}

private highlightResponse(response: HttpResponse, suppressValidation: boolean): string {
private highlightResponse(response: HttpResponse): string {
let code = '';
let nonBodyPart = `HTTP/${response.httpVersion} ${response.statusCode} ${response.statusMessage}
let previewOption = this.settings.previewOption;
if (previewOption === PreviewOption.Exchange) {
// for add request details
let request = response.request;
let requestNonBodyPart = `${request.method} ${request.url} HTTP/1.1
${HttpResponseTextDocumentContentProvider.formatHeaders(request.headers)}`;
code += hljs.highlight('http', requestNonBodyPart + '\r\n').value;
if (request.body) {
let requestContentType = request.getRequestHeaderValue("content-type");
if (typeof request.body !== 'string') {
request.body = 'NOTE: Request Body From File Not Shown';
}
let requestBodyPart = `${ResponseFormatUtility.FormatBody(request.body.toString(), requestContentType, false)}`;
let bodyLanguageAlias = HttpResponseTextDocumentContentProvider.getHighlightLanguageAlias(requestContentType);
if (bodyLanguageAlias) {
code += hljs.highlight(bodyLanguageAlias, requestBodyPart).value;
} else {
code += hljs.highlightAuto(requestBodyPart).value;
}
code += '\r\n';
}

code += '\r\n'.repeat(2);
}

if (previewOption !== PreviewOption.Body) {
let responseNonBodyPart = `HTTP/${response.httpVersion} ${response.statusCode} ${response.statusMessage}
${HttpResponseTextDocumentContentProvider.formatHeaders(response.headers)}`;
code += hljs.highlight('http', nonBodyPart + '\r\n').value;
let contentType = response.getResponseHeaderValue("content-type");
let bodyPart = `${ResponseFormatUtility.FormatBody(response.body, contentType, suppressValidation)}`;
let bodyLanguageAlias = HttpResponseTextDocumentContentProvider.getHighlightLanguageAlias(contentType);
if (bodyLanguageAlias) {
code += hljs.highlight(bodyLanguageAlias, bodyPart).value;
} else {
code += hljs.highlightAuto(bodyPart).value;
code += hljs.highlight('http', responseNonBodyPart + (previewOption !== PreviewOption.Headers ? '\r\n' : '')).value;
}

if (previewOption !== PreviewOption.Headers) {
let responseContentType = response.getResponseHeaderValue("content-type");
let responseBodyPart = `${ResponseFormatUtility.FormatBody(response.body, responseContentType, this.settings.suppressResponseBodyContentTypeValidationWarning)}`;
let bodyLanguageAlias = HttpResponseTextDocumentContentProvider.getHighlightLanguageAlias(responseContentType);
if (bodyLanguageAlias) {
code += hljs.highlight(bodyLanguageAlias, responseBodyPart).value;
} else {
code += hljs.highlightAuto(responseBodyPart).value;
}
}

return code;
}

Expand Down

0 comments on commit c46e7c1

Please sign in to comment.