Skip to content

Commit 5f55fba

Browse files
committed
Restore default rules at startup, even for Pro users
Previously, if Pro users had removed default rules, they were removed entirely until the rules were reset, which could be confusing, or problematic when new default rules are added (e.g. the new websocket passthrough rule - without recreating this, Pro user's websockets would all break). Now, we recreate/reset the default group if it's not present at startup or has been modified/doesn't match the default somehow.
1 parent 79483fe commit 5f55fba

File tree

3 files changed

+75
-63
lines changed

3 files changed

+75
-63
lines changed

src/model/rules/rule-creation.ts

Lines changed: 63 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -131,71 +131,78 @@ export function buildRuleFromExchange(exchange: HttpExchange): HtkMockRule {
131131
};
132132
}
133133

134-
export const buildDefaultGroup = (items: HtkMockItem[]): HtkMockRuleGroup => ({
134+
export const buildDefaultGroupWrapper = (items: HtkMockItem[]): HtkMockRuleGroup => ({
135135
id: 'default-group',
136136
title: "Default rules",
137137
collapsed: true,
138138
items: items
139-
})
139+
});
140+
141+
export const buildDefaultGroupRules = (
142+
rulesStore: RulesStore,
143+
proxyStore: ProxyStore
144+
): HtkMockItem[] => [
145+
// Respond to amiusing.httptoolkit.tech with an emphatic YES
146+
{
147+
id: 'default-amiusing',
148+
type: 'http',
149+
activated: true,
150+
matchers: [
151+
new HttpRule.MethodMatchers.GET(),
152+
new HttpRule.AmIUsingMatcher()
153+
],
154+
completionChecker: new completionCheckers.Always(),
155+
handler: new HttpRule.StaticResponseHandler(200, undefined, amIUsingHtml, {
156+
'content-type': 'text/html',
157+
'cache-control': 'no-store',
158+
'httptoolkit-active': 'true'
159+
})
160+
},
161+
162+
// Share the server certificate on a convenient URL, assuming it supports that
163+
...(versionSatisfies(proxyStore.serverVersion, FROM_FILE_HANDLER_SERVER_RANGE)
164+
? [{
165+
id: 'default-certificate',
166+
type: 'http' as 'http',
167+
activated: true,
168+
matchers: [
169+
new HttpRule.MethodMatchers.GET(),
170+
new matchers.SimplePathMatcher("amiusing.httptoolkit.tech/certificate")
171+
],
172+
completionChecker: new completionCheckers.Always(),
173+
handler: new HttpRule.FromFileResponseHandler(200, undefined, proxyStore.certPath, {
174+
'content-type': 'application/x-x509-ca-cert'
175+
})
176+
}] : []
177+
),
178+
179+
// Pass through all other traffic to the real target
180+
{
181+
id: 'default-wildcard',
182+
type: 'http',
183+
activated: true,
184+
matchers: [new HttpRule.DefaultWildcardMatcher()],
185+
completionChecker: new completionCheckers.Always(),
186+
handler: new HttpRule.PassThroughHandler(rulesStore)
187+
},
188+
{
189+
id: 'default-ws-wildcard',
190+
type: 'websocket',
191+
activated: true,
192+
matchers: [new DefaultWebSocketWildcardMatcher()],
193+
completionChecker: new completionCheckers.Always(),
194+
handler: new WebSocketPassThroughHandler(rulesStore)
195+
}
196+
];
140197

141-
export const buildDefaultRules = (rulesStore: RulesStore, proxyStore: ProxyStore) => ({
198+
export const buildDefaultRulesRoot = (rulesStore: RulesStore, proxyStore: ProxyStore) => ({
142199
id: 'root',
143200
title: "HTTP Toolkit Rules",
144201
isRoot: true,
145202
items: [
146-
buildDefaultGroup([
147-
// Respond to amiusing.httptoolkit.tech with an emphatic YES
148-
{
149-
id: 'default-amiusing',
150-
type: 'http',
151-
activated: true,
152-
matchers: [
153-
new HttpRule.MethodMatchers.GET(),
154-
new HttpRule.AmIUsingMatcher()
155-
],
156-
completionChecker: new completionCheckers.Always(),
157-
handler: new HttpRule.StaticResponseHandler(200, undefined, amIUsingHtml, {
158-
'content-type': 'text/html',
159-
'cache-control': 'no-store',
160-
'httptoolkit-active': 'true'
161-
})
162-
},
163-
164-
// Share the server certificate on a convenient URL, assuming it supports that
165-
...(versionSatisfies(proxyStore.serverVersion, FROM_FILE_HANDLER_SERVER_RANGE)
166-
? [{
167-
id: 'default-certificate',
168-
type: 'http' as 'http',
169-
activated: true,
170-
matchers: [
171-
new HttpRule.MethodMatchers.GET(),
172-
new matchers.SimplePathMatcher("amiusing.httptoolkit.tech/certificate")
173-
],
174-
completionChecker: new completionCheckers.Always(),
175-
handler: new HttpRule.FromFileResponseHandler(200, undefined, proxyStore.certPath, {
176-
'content-type': 'application/x-x509-ca-cert'
177-
})
178-
}] : []
179-
),
180-
181-
// Pass through all other traffic to the real target
182-
{
183-
id: 'default-wildcard',
184-
type: 'http',
185-
activated: true,
186-
matchers: [new HttpRule.DefaultWildcardMatcher()],
187-
completionChecker: new completionCheckers.Always(),
188-
handler: new HttpRule.PassThroughHandler(rulesStore)
189-
},
190-
{
191-
id: 'default-ws-wildcard',
192-
type: 'websocket',
193-
activated: true,
194-
matchers: [new DefaultWebSocketWildcardMatcher()],
195-
completionChecker: new completionCheckers.Always(),
196-
handler: new WebSocketPassThroughHandler(rulesStore)
197-
}
198-
])
203+
buildDefaultGroupWrapper(
204+
buildDefaultGroupRules(rulesStore, proxyStore)
205+
)
199206
]
200207
} as HtkMockRuleRoot);
201208

src/model/rules/rule-migrations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as _ from 'lodash';
22
import * as dedent from 'dedent';
33

44
import { isRuleGroup } from './rules-structure';
5-
import { buildDefaultGroup } from './rule-creation';
5+
import { buildDefaultGroupWrapper } from './rule-creation';
66

77
// Take some raw serialized rule data, exported from any version of the app since HTTP Mock was
88
// launched, and convert it into raw modern rule data, ready to be deserialized.
@@ -22,7 +22,7 @@ export function migrateRuleData(data: any) {
2222
if (defaultRules.length) {
2323
data.items = [
2424
...otherRules,
25-
buildDefaultGroup(defaultRules)
25+
buildDefaultGroupWrapper(defaultRules)
2626
];
2727
} else {
2828
data.items = otherRules;

src/model/rules/rules-store.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ import {
5959
areItemsEqual,
6060
} from './rules-structure';
6161
import {
62-
buildDefaultGroup,
63-
buildDefaultRules,
62+
buildDefaultGroupRules,
63+
buildDefaultGroupWrapper,
64+
buildDefaultRulesRoot,
6465
buildForwardingRuleIntegration,
6566
} from './rule-creation';
6667
import {
@@ -214,6 +215,10 @@ export class RulesStore {
214215
} else {
215216
// Drafts are never persisted, so always need resetting to match the just-loaded data:
216217
this.resetRuleDrafts();
218+
219+
// Recreate default rules on startup, even if we're restoring persisted rules
220+
const defaultRules = buildDefaultGroupRules(this, this.proxyStore);
221+
defaultRules.forEach(r => this.ensureRuleExists(r));
217222
}
218223
} else {
219224
// For free users, reset rules to default (separately, so defaults can use settings loaded above)
@@ -371,7 +376,7 @@ export class RulesStore {
371376
@action.bound
372377
resetRulesToDefault() {
373378
// Set the rules back to the default settings
374-
this.rules = buildDefaultRules(this, this.proxyStore);
379+
this.rules = buildDefaultRulesRoot(this, this.proxyStore);
375380
this.resetRuleDrafts();
376381
}
377382

@@ -382,7 +387,7 @@ export class RulesStore {
382387

383388
@computed
384389
get areSomeRulesNonDefault() {
385-
const defaultRules = buildDefaultRules(this, this.proxyStore);
390+
const defaultRules = buildDefaultRulesRoot(this, this.proxyStore);
386391
return !_.isEqualWith(this.draftRules, defaultRules, areItemsEqual);
387392
}
388393

@@ -686,7 +691,7 @@ export class RulesStore {
686691
let draftDefaultGroupPath = findItemPath(this.draftRules, { id: 'default-group' });
687692
if (!draftDefaultGroupPath) {
688693
// If there's no draft default rules at all, build one
689-
this.draftRules.items.push(buildDefaultGroup([rule]));
694+
this.draftRules.items.push(buildDefaultGroupWrapper([rule]));
690695
draftDefaultGroupPath = [this.draftRules.items.length - 1];
691696
} else {
692697
const draftDefaultGroup = getItemAtPath(this.draftRules, draftDefaultGroupPath) as HtkMockRuleGroup;

0 commit comments

Comments
 (0)