A bunch of fixes with OAuth and minor network bugs (#155)

* A bunch of fixes with OAuth and minor network bugs

* Handle charsets and only autocomplete constants if exact prefix

* Fix head requests

* Fix double submisson of request

* Fixed tests
This commit is contained in:
Gregory Schier
2017-04-19 18:37:40 -07:00
committed by GitHub
parent 2f738d6635
commit cb2e720c5c
17 changed files with 56 additions and 30 deletions

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
[*.{js,json,html}]
charset = utf-8
indent_style = space
indent_size = 2

View File

@@ -88,6 +88,7 @@ Curl.option = {
DEBUGFUNCTION: 'DEBUGFUNCTION',
ACCEPT_ENCODING: 'ACCEPT_ENCODING',
FOLLOWLOCATION: 'FOLLOWLOCATION',
NOBODY: 'NOBODY',
HTTPAUTH: 'HTTPAUTH',
HTTPHEADER: 'HTTPHEADER',
HTTPPOST: 'HTTPPOST',

View File

@@ -121,6 +121,7 @@ describe('updateMimeType()', async () => {
expect(request).not.toBeNull();
const newRequest = await requestModel.updateMimeType(request, null);
expect(newRequest.body).toEqual({});
expect(newRequest.headers).toEqual([]);
});
});

View File

@@ -144,12 +144,12 @@ export function updateMimeType (request, mimeType, doCreate = false) {
// 1. Update Content-Type header //
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
if (!mimeType) {
// Remove the contentType header if we are un-setting it
const hasBody = typeof mimeType === 'string';
if (!hasBody) {
headers = headers.filter(h => h !== contentTypeHeader);
} else if (contentTypeHeader) {
} else if (mimeType && contentTypeHeader) {
contentTypeHeader.value = mimeType;
} else {
} else if (mimeType && !contentTypeHeader) {
headers.push({name: 'Content-Type', value: mimeType});
}

View File

@@ -71,6 +71,7 @@ describe('actuallySend()', () => {
],
CUSTOMREQUEST: 'POST',
ACCEPT_ENCODING: '',
NOBODY: 0,
FOLLOWLOCATION: true,
HTTPHEADER: [
'Content-Type: application/json',
@@ -161,6 +162,7 @@ describe('actuallySend()', () => {
USERNAME: 'user',
PASSWORD: 'pass',
POSTFIELDS: 'foo=bar',
NOBODY: 0,
PROXY: '',
TIMEOUT_MS: 0,
URL: 'http://localhost/?foo%20bar=hello%26world',
@@ -214,6 +216,7 @@ describe('actuallySend()', () => {
],
NOPROGRESS: false,
PROXY: '',
NOBODY: 0,
TIMEOUT_MS: 0,
UPLOAD: 1,
URL: 'http://localhost/',
@@ -273,6 +276,7 @@ describe('actuallySend()', () => {
],
NOPROGRESS: false,
PROXY: '',
NOBODY: 0,
TIMEOUT_MS: 0,
URL: 'http://localhost/',
USERAGENT: `insomnia/${getAppVersion()}`,

View File

@@ -8,7 +8,7 @@ import * as models from '../models';
import * as querystring from '../common/querystring';
import * as util from '../common/misc.js';
import {AUTH_BASIC, AUTH_DIGEST, AUTH_NTLM, CONTENT_TYPE_FORM_DATA, CONTENT_TYPE_FORM_URLENCODED, DEBOUNCE_MILLIS, getAppVersion} from '../common/constants';
import {describeByteSize, hasAcceptHeader, hasAuthHeader, hasContentTypeHeader, hasUserAgentHeader, setDefaultProtocol} from '../common/misc';
import {describeByteSize, hasAuthHeader, hasContentTypeHeader, hasUserAgentHeader, setDefaultProtocol} from '../common/misc';
import {getRenderedRequest} from '../common/render';
import fs from 'fs';
import * as db from '../common/database';
@@ -84,10 +84,12 @@ export function _actuallySend (renderedRequest, workspace, settings) {
// Set all the basic options
setOpt(Curl.option.CUSTOMREQUEST, renderedRequest.method);
setOpt(Curl.option.NOBODY, renderedRequest.method.toLowerCase() === 'head' ? 1 : 0);
setOpt(Curl.option.FOLLOWLOCATION, settings.followRedirects);
setOpt(Curl.option.TIMEOUT_MS, settings.timeout); // 0 for no timeout
setOpt(Curl.option.VERBOSE, true); // True so debug function works
setOpt(Curl.option.NOPROGRESS, false); // False so progress function works
setOpt(Curl.option.ACCEPT_ENCODING, ''); // Auto decode everything
// Setup debug handler
setOpt(Curl.option.DEBUGFUNCTION, (infoType, content) => {
@@ -371,11 +373,6 @@ export function _actuallySend (renderedRequest, workspace, settings) {
setOpt(Curl.option.USERAGENT, `insomnia/${getAppVersion()}`);
}
// Set Accept encoding
if (!hasAcceptHeader(headers)) {
setOpt(Curl.option.ACCEPT_ENCODING, ''); // Accept anything
}
// Prevent curl from adding default content-type header
if (!hasContentTypeHeader(headers)) {
headers.push({name: 'content-type', value: ''});

View File

@@ -2,6 +2,7 @@ export const GRANT_TYPE_AUTHORIZATION_CODE = 'authorization_code';
export const GRANT_TYPE_IMPLICIT = 'implicit';
export const GRANT_TYPE_PASSWORD = 'password';
export const GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials';
export const GRANT_TYPE_REFRESH = 'refresh_token';
export const RESPONSE_TYPE_CODE = 'code';
export const RESPONSE_TYPE_TOKEN = 'token';

View File

@@ -139,7 +139,7 @@ async function _getAccessToken (requestId, authentication, forceRefresh) {
authentication.credentialsInBody,
authentication.clientId,
authentication.clientSecret,
authentication.refreshToken,
token.refreshToken,
authentication.scope
);

View File

@@ -17,7 +17,7 @@ export function responseToObject (body, keys) {
let results = {};
for (const key of keys) {
const value = typeof data[key] === 'string' ? data[key] : null;
const value = data[key] !== undefined ? data[key] : null;
results[key] = value;
}

View File

@@ -10,7 +10,7 @@ export default async function (accessTokenUrl,
refreshToken,
scope = '') {
const params = [
{name: c.P_GRANT_TYPE, value: c.GRANT_TYPE_IMPLICIT},
{name: c.P_GRANT_TYPE, value: c.GRANT_TYPE_REFRESH},
{name: c.P_REFRESH_TOKEN, value: refreshToken}
];

View File

@@ -3,6 +3,7 @@ import NeDB from 'nedb';
import fsPath from 'path';
import crypto from 'crypto';
import * as util from '../common/misc';
import {DB_PERSIST_INTERVAL} from '../common/constants';
const TYPE_RESOURCE = 'Resource';
const TYPE_CONFIG = 'Config';
@@ -181,6 +182,10 @@ export function initDB (config, forceReset) {
Object.assign({filename: configPath, autoload: true}, config)
);
for (const key of Object.keys(_database)) {
_database[key].persistence.setAutocompactionInterval(DB_PERSIST_INTERVAL);
}
// Done
console.log(`-- Initialize Sync DB at ${basePath} --`);
}

View File

@@ -178,6 +178,7 @@ function hint (cm, options) {
const nameMatchLong = previousText.match(NAME_MATCH_FLEXIBLE);
const nameSegment = nameMatch ? nameMatch[0] : fallbackSegment;
const nameSegmentLong = nameMatchLong ? nameMatchLong[0] : fallbackSegment;
const nameSegmentFull = previousText;
// Actually try to match the list of things
const allShortMatches = [];
@@ -195,9 +196,8 @@ function hint (cm, options) {
// TODO: Make this more flexible. This is really only here as a hack to make
// constants only match full string prefixes.
if (allowMatchingConstants) {
// matchSegments(constantsToMatch, nameSegment, TYPE_CONSTANT, MAX_CONSTANTS)
// .map(m => allShortMatches.push(m));
matchSegments(constantsToMatch, nameSegmentLong, TYPE_CONSTANT, MAX_CONSTANTS)
// Only match full segments with constants
matchSegments(constantsToMatch, nameSegmentFull, TYPE_CONSTANT, MAX_CONSTANTS)
.map(m => allLongMatches.push(m));
}

View File

@@ -190,6 +190,8 @@ class OneLineEditor extends PureComponent {
for (let i = 0; i < 20 && node; i++) {
if (node.tagName === 'FORM') {
node.dispatchEvent(new window.Event('submit'));
e.preventDefault();
e.stopPropagation();
break;
}
node = node.parentNode;
@@ -254,7 +256,7 @@ class OneLineEditor extends PureComponent {
}
_mayContainNunjucks (text) {
return !!text.match(NUNJUCKS_REGEX);
return !!(text && text.match(NUNJUCKS_REGEX));
}
render () {

View File

@@ -1,4 +1,5 @@
import React, {PropTypes, PureComponent} from 'react';
import moment from 'moment';
import autobind from 'autobind-decorator';
import OneLineEditor from '../../codemirror/one-line-editor';
import * as misc from '../../../../common/misc';
@@ -288,11 +289,13 @@ class OAuth2 extends PureComponent {
return null;
}
if (!token.expireAt) {
if (!token.expiresAt) {
return '(never expires)';
}
return `(expires ${new Date(token.expireAt)})`;
const expiresAt = new Date(token.expiresAt);
const str = moment(expiresAt).fromNow();
return <span title={expiresAt.toString()}>(expires {str})</span>;
}
render () {
@@ -345,7 +348,6 @@ class OAuth2 extends PureComponent {
<label>
<small>
Refresh Token
{(tok && tok.refreshToken) ? <em>&nbsp;{expireLabel}</em> : null}
</small>
<input value={(tok && tok.refreshToken) || ''}
placeholder="n/a"
@@ -355,8 +357,7 @@ class OAuth2 extends PureComponent {
<div className="form-control form-control--outlined">
<label>
<small>
Access Token
{(tok && !tok.refreshToken) ? <em>&nbsp;{expireLabel}</em> : null}
Access Token {tok ? <em>{expireLabel}</em> : null}
</small>
<input value={(tok && tok.accessToken) || ''}
placeholder="n/a"

View File

@@ -31,10 +31,10 @@ class RequestHeadersEditor extends PureComponent {
const rows = headersString.split(/[\n,]+/);
for (const row of rows) {
const items = row.split(':');
const [rawName, ...items] = row.split(':');
const name = (items[0] || '').trim();
const value = (items[1] || '').trim();
const name = (rawName || '').trim();
const value = (items.join(':')).trim();
headers.push({name, value});
}

View File

@@ -1,4 +1,5 @@
import React, {PropTypes, PureComponent} from 'react';
import iconv from 'iconv-lite';
import autobind from 'autobind-decorator';
import {shell} from 'electron';
import CodeEditor from '../codemirror/code-editor';
@@ -141,24 +142,31 @@ class ResponseViewer extends PureComponent {
</div>
);
} else if (previewMode === PREVIEW_MODE_FRIENDLY && ct.includes('html')) {
const justContentType = contentType.split(';')[0];
const match = contentType.match(/charset=([\w-]+)/);
const charset = (match && match.length >= 2) ? match[1] : 'utf-8';
return (
<ResponseWebView
body={bodyBuffer.toString('utf8')}
contentType={contentType}
body={iconv.decode(bodyBuffer, charset)}
contentType={`${justContentType}; charset=UTF-8`}
url={url}
/>
);
} else if (previewMode === PREVIEW_MODE_RAW) {
const match = contentType.match(/charset=([\w-]+)/);
const charset = (match && match.length >= 2) ? match[1] : 'utf-8';
return (
<ResponseRaw
value={bodyBuffer.toString('utf8')}
value={iconv.decode(bodyBuffer, charset)}
fontSize={editorFontSize}
/>
);
} else { // Show everything else as "source"
let mode = contentType;
const body = bodyBuffer.toString('utf8');
const match = contentType.match(/charset=([\w-]+)/);
const charset = (match && match.length >= 2) ? match[1] : 'utf-8';
const body = iconv.decode(bodyBuffer, charset);
let mode = contentType;
try {
// FEATURE: Detect JSON even without content-type
contentType.indexOf('json') === -1 && JSON.parse(body);

View File

@@ -109,11 +109,13 @@
"hkdf": "0.0.2",
"html-entities": "^1.2.0",
"httpsnippet": "1.16.5",
"iconv-lite": "^0.4.15",
"insomnia-importers": "~1.3.1",
"jsonlint": "~1.6.2",
"jsonpath": "~0.2.11",
"mime-types": "~2.1.14",
"mkdirp": "~0.5.1",
"moment": "^2.18.1",
"nedb": "~1.8.0",
"node-forge": "~0.7.0",
"node-libcurl": "git://github.com/getinsomnia/node-libcurl.git#3fc2afea435f3548eedbef9c68d3fee642dfcddb",