Add ability to specify HTTP version (#2276)

* Add ability to specify HTTP version (Closes #2245)

* Fix default HTTP version

* Format w/ Prettier
This commit is contained in:
Gregory Schier 2020-06-10 11:15:56 -07:00 committed by GitHub
parent ec81568d43
commit 53c21fba37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 159 additions and 17 deletions

View File

@ -6,6 +6,7 @@ import { CurlCode } from 'node-libcurl/dist/enum/CurlCode';
import { CurlInfoDebug } from 'node-libcurl/dist/enum/CurlInfoDebug';
import { CurlFeature } from 'node-libcurl/dist/enum/CurlFeature';
import { CurlNetrc } from 'node-libcurl/dist/enum/CurlNetrc';
import { CurlHttpVersion } from 'node-libcurl/dist/enum/CurlHttpVersion';
class Curl extends EventEmitter {
constructor() {
@ -130,6 +131,7 @@ Curl.option = {
HTTPGET: 'HTTPGET',
HTTPHEADER: 'HTTPHEADER',
HTTPPOST: 'HTTPPOST',
HTTP_VERSION: 'HTTP_VERSION',
INFILESIZE_LARGE: 'INFILESIZE_LARGE',
KEYPASSWD: 'KEYPASSWD',
MAXREDIRS: 'MAXREDIRS',
@ -185,4 +187,5 @@ module.exports = {
CurlInfoDebug: getTsEnumOnlyWithNamedMembers(CurlInfoDebug),
CurlFeature: getTsEnumOnlyWithNamedMembers(CurlFeature),
CurlNetrc: getTsEnumOnlyWithNamedMembers(CurlNetrc),
CurlHttpVersion: getTsEnumOnlyWithNamedMembers(CurlHttpVersion),
};

View File

@ -227,6 +227,17 @@ export const HAWK_ALGORITHM_SHA1 = 'sha1';
export const JSON_ORDER_PREFIX = '&';
export const JSON_ORDER_SEPARATOR = '~|';
// HTTP version codes
export const HttpVersions = {
V1_0: 'V1_0',
V1_1: 'V1_1',
V2_0: 'V2_0',
v3: 'v3',
default: 'default',
};
export type HttpVersion = $Keys<typeof HttpVersions>;
const authTypesMap = {
[AUTH_BASIC]: ['Basic', 'Basic Auth'],
[AUTH_DIGEST]: ['Digest', 'Digest Auth'],

View File

@ -1,8 +1,9 @@
// @flow
import type { BaseModel } from './index';
import * as db from '../common/database';
import { getAppDefaultTheme, UPDATE_CHANNEL_STABLE } from '../common/constants';
import { getAppDefaultTheme, HttpVersions, UPDATE_CHANNEL_STABLE } from '../common/constants';
import * as hotkeys from '../common/hotkeys';
import type { HttpVersion } from '../common/constants';
export type PluginConfig = {
disabled: boolean,
@ -17,8 +18,8 @@ type BaseSettings = {
autocompleteDelay: number,
deviceId: string | null,
disableHtmlPreviewJs: boolean,
disableUpdateNotification: boolean,
disableResponsePreviewLinks: boolean,
disableUpdateNotification: boolean,
editorFontSize: number,
editorIndentSize: number,
editorIndentWithTabs: boolean,
@ -26,6 +27,7 @@ type BaseSettings = {
editorLineWrapping: boolean,
enableAnalytics: boolean,
environmentHighlightColorStyle: string,
filterResponsesByEnv: boolean,
followRedirects: boolean,
fontInterface: string | null,
fontMonospace: string | null,
@ -40,9 +42,10 @@ type BaseSettings = {
maxTimelineDataSizeKB: number,
noProxy: string,
nunjucksPowerUserMode: boolean,
pluginConfig: PluginConfigMap,
pluginPath: string,
preferredHttpVersion: HttpVersion,
proxyEnabled: boolean,
filterResponsesByEnv: boolean,
showPasswords: boolean,
theme: string,
timeout: number,
@ -51,7 +54,6 @@ type BaseSettings = {
useBulkHeaderEditor: boolean,
useBulkParametersEditor: boolean,
validateSSL: boolean,
pluginConfig: PluginConfigMap,
// Feature flags
enableSyncBeta: boolean,
@ -80,6 +82,7 @@ export function init(): BaseSettings {
editorLineWrapping: true,
enableAnalytics: false,
environmentHighlightColorStyle: 'sidebar-indicator',
filterResponsesByEnv: false,
followRedirects: true,
fontInterface: null,
fontMonospace: null,
@ -96,8 +99,8 @@ export function init(): BaseSettings {
nunjucksPowerUserMode: false,
pluginConfig: {},
pluginPath: '',
preferredHttpVersion: HttpVersions.default,
proxyEnabled: false,
filterResponsesByEnv: false,
showPasswords: false,
theme: getAppDefaultTheme(),
timeout: 0,

View File

@ -11,6 +11,7 @@ import {
CONTENT_TYPE_FORM_DATA,
CONTENT_TYPE_FORM_URLENCODED,
getAppVersion,
HttpVersions,
} from '../../common/constants';
import { filterHeaders } from '../../common/misc';
import { globalBeforeEach } from '../../__jest__/before-each';
@ -571,6 +572,50 @@ describe('actuallySend()', () => {
},
});
});
it('sets HTTP version', async () => {
const workspace = await models.workspace.create();
const settings = await models.settings.create();
const request = Object.assign(models.request.init(), {
_id: 'req_123',
parentId: workspace._id,
});
const renderedRequest = await getRenderedRequest(request);
const responseV1 = await networkUtils._actuallySend(renderedRequest, CONTEXT, workspace, {
...settings,
preferredHttpVersion: HttpVersions.V1_0,
});
const responseV11 = await networkUtils._actuallySend(renderedRequest, CONTEXT, workspace, {
...settings,
preferredHttpVersion: HttpVersions.V1_1,
});
const responseV2 = await networkUtils._actuallySend(renderedRequest, CONTEXT, workspace, {
...settings,
preferredHttpVersion: HttpVersions.V2_0,
});
const responseV3 = await networkUtils._actuallySend(renderedRequest, CONTEXT, workspace, {
...settings,
preferredHttpVersion: HttpVersions.v3,
});
const responseDefault = await networkUtils._actuallySend(renderedRequest, CONTEXT, workspace, {
...settings,
preferredHttpVersion: HttpVersions.default,
});
const responseInvalid = await networkUtils._actuallySend(renderedRequest, CONTEXT, workspace, {
...settings,
preferredHttpVersion: 'blah',
});
const r = models.response;
expect(JSON.parse(r.getBodyBuffer(responseV1)).options.HTTP_VERSION).toBe('V1_0');
expect(JSON.parse(r.getBodyBuffer(responseV11)).options.HTTP_VERSION).toBe('V1_1');
expect(JSON.parse(r.getBodyBuffer(responseV2)).options.HTTP_VERSION).toBe('V2_0');
expect(JSON.parse(r.getBodyBuffer(responseV3)).options.HTTP_VERSION).toBe('v3');
expect(JSON.parse(r.getBodyBuffer(responseDefault)).options.HTTP_VERSION).toBe(undefined);
expect(JSON.parse(r.getBodyBuffer(responseInvalid)).options.HTTP_VERSION).toBe(undefined);
});
});
describe('_getAwsAuthHeaders', () => {

View File

@ -13,7 +13,15 @@ import mkdirp from 'mkdirp';
import crypto from 'crypto';
import clone from 'clone';
import { parse as urlParse, resolve as urlResolve } from 'url';
import { Curl, CurlAuth, CurlCode, CurlInfoDebug, CurlFeature, CurlNetrc } from 'node-libcurl';
import {
Curl,
CurlAuth,
CurlCode,
CurlInfoDebug,
CurlFeature,
CurlNetrc,
CurlHttpVersion,
} from 'node-libcurl';
import { join as pathJoin } from 'path';
import uuid from 'uuid';
import * as models from '../models';
@ -27,6 +35,7 @@ import {
CONTENT_TYPE_FORM_URLENCODED,
getAppVersion,
getTempDir,
HttpVersions,
STATUS_CODE_PLUGIN_ERROR,
} from '../common/constants';
import {
@ -338,8 +347,34 @@ export async function _actuallySend(
setOpt(Curl.option.URL, finalUrl);
}
addTimelineText('Preparing request to ' + finalUrl);
addTimelineText(`Using ${Curl.getVersion()}`);
addTimelineText('Current time is ' + new Date().toISOString());
addTimelineText(`Using ${Curl.getVersion()}`);
// Set HTTP version
switch (settings.preferredHttpVersion) {
case HttpVersions.V1_0:
addTimelineText('Using HTTP 1.0');
setOpt(Curl.option.HTTP_VERSION, CurlHttpVersion.V1_0);
break;
case HttpVersions.V1_1:
addTimelineText('Using HTTP 1.1');
setOpt(Curl.option.HTTP_VERSION, CurlHttpVersion.V1_1);
break;
case HttpVersions.V2_0:
addTimelineText('Using HTTP/2');
setOpt(Curl.option.HTTP_VERSION, CurlHttpVersion.V2_0);
break;
case HttpVersions.v3:
addTimelineText('Using HTTP/3');
setOpt(Curl.option.HTTP_VERSION, CurlHttpVersion.v3);
break;
case HttpVersions.default:
addTimelineText('Using default HTTP version');
break;
default:
addTimelineText(`Unknown HTTP version specified ${settings.preferredHttpVersion}`);
break;
}
// Set timeout
if (settings.timeout > 0) {

View File

@ -9,6 +9,7 @@ import {
EDITOR_KEY_MAP_EMACS,
EDITOR_KEY_MAP_SUBLIME,
EDITOR_KEY_MAP_VIM,
HttpVersions,
isLinux,
isMac,
isWindows,
@ -20,6 +21,7 @@ import { setFont } from '../../../plugins/misc';
import * as session from '../../../account/session';
import Tooltip from '../tooltip';
import CheckForUpdatesButton from '../check-for-updates-button';
import type { HttpVersion } from '../../../common/constants';
// Font family regex to match certain monospace fonts that don't get
// recognized as monospace
@ -81,11 +83,6 @@ class General extends React.PureComponent<Props, State> {
return this.props.updateSetting(el.name, value);
}
async _handleToggleMenuBar(e: SyntheticEvent<HTMLInputElement>) {
const settings = await this._handleUpdateSetting(e);
this.props.handleToggleMenuBar(settings.autoHideMenuBar);
}
async _handleUpdateSettingAndRestart(e: SyntheticEvent<HTMLInputElement>) {
await this._handleUpdateSetting(e);
const { app } = electron.remote || electron;
@ -93,11 +90,6 @@ class General extends React.PureComponent<Props, State> {
app.exit();
}
async _handleFontLigatureChange(el: SyntheticEvent<HTMLInputElement>) {
const settings = await this._handleUpdateSetting(el);
setFont(settings);
}
async _handleFontSizeChange(el: SyntheticEvent<HTMLInputElement>) {
const settings = await this._handleUpdateSetting(el);
setFont(settings);
@ -108,6 +100,32 @@ class General extends React.PureComponent<Props, State> {
setFont(settings);
}
renderEnumSetting(
label: string,
name: string,
values: Array<{ name: string, value: any }>,
help: string,
forceRestart?: boolean,
) {
const { settings } = this.props;
const onChange = forceRestart ? this._handleUpdateSettingAndRestart : this._handleUpdateSetting;
return (
<div className="form-control form-control--outlined pad-top-sm">
<label>
{label}
{help && <HelpTooltip className="space-left">{help}</HelpTooltip>}
<select value={settings[name] || '__NULL__'} name={name} onChange={onChange}>
{values.map(({ name, value }) => (
<option key={value} value={value}>
{name}
</option>
))}
</select>
</label>
</div>
);
}
renderBooleanSetting(label: string, name: string, help: string, forceRestart?: boolean) {
const { settings } = this.props;
@ -330,6 +348,24 @@ class General extends React.PureComponent<Props, State> {
</div>
</div>
<div className="form-row pad-top-sm">
{this.renderEnumSetting(
'Preferred HTTP version',
'preferredHttpVersion',
([
{ name: 'Default', value: HttpVersions.default },
{ name: 'HTTP 1.0', value: HttpVersions.V1_0 },
{ name: 'HTTP 1.1', value: HttpVersions.V1_1 },
{ name: 'HTTP/2', value: HttpVersions.V2_0 },
// Enable when our version of libcurl supports HTTP/3
// { name: 'HTTP/3', value: HttpVersions.v3 },
]: Array<{ name: string, value: HttpVersion }>),
'Preferred HTTP version to use for requests which will fall back if it cannot be' +
'negotiated',
)}
</div>
<div className="form-row pad-top-sm">
{this.renderNumberSetting('Maximum Redirects', 'maxRedirects', '-1 for infinite', {
min: -1,

View File

@ -32,6 +32,15 @@ declare module 'node-libcurl' {
CurlCode: {|
CURLE_ABORTED_BY_CALLBACK: number,
|};
CurlHttpVersion: {|
None: number,
V1_0: number,
V1_1: number,
V2PriorKnowledge: number,
V2Tls: number,
V2_0: number,
v3: number,
|};
CurlInfoDebug: {|
Text: number,
HeaderIn: number,