Skip to content

Commit 621cb76

Browse files
author
Florian Rappl
committed
Enhanced API surface
1 parent 064c684 commit 621cb76

8 files changed

Lines changed: 163 additions & 69 deletions

File tree

src/server/core/recorder.ts

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { KrasRecorder, KrasRequest, KrasAnswer, RecordedRequest, RecordedError,
44

55
export class Recorder extends EventEmitter implements KrasRecorder {
66
private readonly maximum: number;
7+
private enabled = true;
78
readonly requests: Array<RecordedRequest> = [];
89
readonly errors: Array<RecordedError> = [];
910
readonly messages: Array<RecordedMessage> = [];
@@ -13,47 +14,57 @@ export class Recorder extends EventEmitter implements KrasRecorder {
1314
this.maximum = maximum;
1415
}
1516

17+
disable() {
18+
this.enabled = false;
19+
}
20+
1621
hit(start: Date, end: Date, request: KrasRequest, response: KrasAnswer) {
17-
const requests = this.requests;
18-
const id = uuid();
19-
const item = {
20-
id,
21-
start,
22-
end,
23-
request,
24-
response,
25-
};
26-
27-
if (requests.length === this.maximum) {
28-
requests.shift();
29-
}
22+
if (this.enabled) {
23+
const requests = this.requests;
24+
const id = uuid();
25+
const item = {
26+
id,
27+
start,
28+
end,
29+
request,
30+
response,
31+
};
3032

31-
this.requests.push(item);
32-
this.emit('recorded-request', item);
33+
if (requests.length === this.maximum) {
34+
requests.shift();
35+
}
36+
37+
this.requests.push(item);
38+
this.emit('recorded-request', item);
39+
}
3340
}
3441

3542
message(time: Date, data: { content: string, from: string, to: string }) {
36-
const id = uuid();
37-
const item = {
38-
id,
39-
time,
40-
...data,
41-
};
42-
43-
this.messages.push(item);
44-
this.emit('recorded-message', item);
43+
if (this.enabled) {
44+
const id = uuid();
45+
const item = {
46+
id,
47+
time,
48+
...data,
49+
};
50+
51+
this.messages.push(item);
52+
this.emit('recorded-message', item);
53+
}
4554
}
4655

4756
miss(start: Date, end: Date, request: KrasRequest) {
48-
const id = uuid();
49-
const item = {
50-
id,
51-
start,
52-
end,
53-
request,
54-
};
55-
56-
this.errors.push(item);
57-
this.emit('recorded-miss', item);
57+
if (this.enabled) {
58+
const id = uuid();
59+
const item = {
60+
id,
61+
start,
62+
end,
63+
request,
64+
};
65+
66+
this.errors.push(item);
67+
this.emit('recorded-miss', item);
68+
}
5869
}
5970
}

src/server/core/webserver.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,14 @@ export class WebServer extends EventEmitter implements BaseKrasServer {
7575
ws,
7676
target,
7777
url,
78+
req,
7879
});
7980
ws.on('close', () => this.emit('user-disconnected', {
8081
id,
8182
ws,
8283
target,
8384
url,
85+
req,
8486
}));
8587
}));
8688
}

src/server/helpers/middlewares.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@ interface RequestHandlerCreator {
66
(...args: Array<any>): KrasServerHandler;
77
}
88

9-
function findMiddleware(modulePath: string): RequestHandlerCreator {
10-
try {
11-
const middlewareCreator = require(modulePath);
12-
13-
if (typeof middlewareCreator === 'function') {
14-
return middlewareCreator;
15-
}
16-
} catch (e) {}
9+
interface RequestHandlerInitializer {
10+
setup?(server: KrasServer, config: KrasConfiguration): void;
11+
}
1712

18-
return undefined;
13+
function findMiddleware(modulePath: string): RequestHandlerCreator | RequestHandlerInitializer {
14+
try {
15+
return require(modulePath);
16+
} catch (e) {
17+
return undefined;
18+
}
1919
}
2020

21-
function createMiddleware(source: string, options: Array<any>) {
21+
function createMiddleware(server: KrasServer, config: KrasConfiguration, source: string, options: Array<any>) {
2222
const creator = findMiddleware(source);
2323

24-
if (creator) {
24+
if (typeof creator === 'function') {
2525
const handler = creator(...options);
2626

2727
if (typeof handler === 'function') {
@@ -32,6 +32,8 @@ function createMiddleware(source: string, options: Array<any>) {
3232
handler,
3333
};
3434
}
35+
} else if (creator && typeof creator.setup === 'function') {
36+
creator.setup(server, config);
3537
}
3638

3739
return undefined;
@@ -53,7 +55,7 @@ export function withMiddlewares(server: KrasServer, config: KrasConfiguration) {
5355
for (const definition of middlewareDefinitions) {
5456
const source = definition.source;
5557
const options = definition.options || [];
56-
const middleware = createMiddleware(source, options);
58+
const middleware = createMiddleware(server, config, source, options);
5759

5860
if (middleware) {
5961
server.middlewares.push(middleware);

src/server/injectors/proxy-injector.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ interface WebSocketSessions {
1717
[id: string]: WebSocket;
1818
}
1919

20+
interface BufferEntry {
21+
time: number;
22+
data: WebSocket.Data;
23+
}
24+
25+
function releaseFrom(buffer: Array<BufferEntry>, ws: WebSocket) {
26+
const item = buffer.shift();
27+
ws.send(item.data);
28+
29+
if (buffer.length) {
30+
const diff = buffer[0].time - item.time;
31+
setTimeout(() => releaseFrom(buffer, ws), diff);
32+
}
33+
}
34+
2035
export default class ProxyInjector implements KrasInjector {
2136
private readonly sessions: WebSocketSessions = {};
2237
private readonly options: KrasInjectorConfig & ProxyInjectorConfig;
@@ -32,16 +47,37 @@ export default class ProxyInjector implements KrasInjector {
3247

3348
core.on('user-connected', e => {
3449
const url = this.map[e.target] + e.url;
50+
let open = false;
51+
const buffer: Array<BufferEntry> = [];
3552
const ws = new WebSocket(url, {
3653
rejectUnauthorized: false,
54+
protocol: e.ws.protocol,
55+
});
56+
ws.on('open', () => {
57+
open = true;
58+
59+
if (buffer.length) {
60+
releaseFrom(buffer, ws);
61+
}
62+
});
63+
ws.on('close', e => {
64+
core.emit('ws-closed', { reason: e });
3765
});
3866
ws.on('message', data => {
3967
core.emit('message', { content: data, from: url, to: e.id });
4068
e.ws.send(data, (err: Error) => core.emit('error', err));
4169
});
4270
e.ws.on('message', (data: WebSocket.Data) => {
4371
core.emit('message', { content: data, to: url, from: e.id });
44-
ws.send(data, (err: Error) => core.emit('error', err));
72+
73+
if (open) {
74+
ws.send(data, (err: Error) => core.emit('error', err));
75+
} else {
76+
buffer.push({
77+
time: Date.now(),
78+
data,
79+
})
80+
}
4581
});
4682
ws.on('error', err => core.emit('error', err.error));
4783
this.sessions[e.id] = ws;

src/server/injectors/script-injector.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { asScript, watch, Watcher } from '../helpers/io';
22
import { basename } from 'path';
33
import { editFileOption, editDirectoryOption } from '../helpers/build-options';
44
import { fromJson } from '../helpers/build-response';
5-
import { KrasRequest, KrasConfiguration, Headers, StoredFileEntry, KrasInjectorConfig, KrasInjector, KrasAnswer, KrasInjectorOptions } from '../types';
5+
import { KrasRequest, KrasConfiguration, StoredFileEntry, KrasInjectorConfig, KrasInjector, KrasAnswer, KrasInjectorOptions, ScriptResponseBuilder } from '../types';
66
import { EventEmitter } from 'events';
77

88
function errorHandler(): undefined {
@@ -27,17 +27,6 @@ export interface DynamicScriptInjectorConfig {
2727
}>;
2828
}
2929

30-
export interface ScriptResponseBuilderData {
31-
statusCode: number;
32-
statusText: string;
33-
headers: Headers;
34-
content: string;
35-
}
36-
37-
export interface ScriptResponseBuilder {
38-
(data: ScriptResponseBuilderData): KrasAnswer;
39-
}
40-
4130
export interface ScriptFileEntry {
4231
active: boolean;
4332
file?: string;

src/server/kras.ts

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,49 @@ import { withFiles } from './helpers/files';
77
import { withMiddlewares } from './helpers/middlewares';
88
import { runWith, configureHandler } from './helpers/fluent';
99
import { currentDir } from './core/info';
10-
import { KrasConfiguration, KrasServer, LogEntry, LogEntryType, LogLevel, KrasInjector, KrasServerMethods, KrasServerHandler, KrasHandlerConfiguration, KrasRunner, KrasMiddleware } from './types';
11-
import { buildConfiguration, mergeConfiguration, readConfiguration, ConfigurationOptions } from './core/config';
12-
export { KrasRequestHandler, KrasInjectorOptions, KrasInjectorOption, KrasInjectorConfig, KrasRequest, KrasResponse, KrasAnswer } from './types';
13-
export { KrasInjector, KrasConfiguration, KrasServerMethods, KrasServerHandler, KrasHandlerConfiguration, KrasRunner };
10+
import {
11+
KrasConfiguration,
12+
KrasServer,
13+
LogEntry,
14+
LogEntryType,
15+
LogLevel,
16+
KrasInjector,
17+
KrasServerMethods,
18+
KrasServerHandler,
19+
KrasHandlerConfiguration,
20+
KrasRunner,
21+
KrasMiddleware
22+
} from './types';
23+
import {
24+
buildConfiguration,
25+
mergeConfiguration,
26+
readConfiguration,
27+
ConfigurationOptions
28+
} from './core/config';
29+
export {
30+
KrasRequestHandler,
31+
KrasInjectorOptions,
32+
KrasInjectorOption,
33+
KrasInjectorConfig,
34+
KrasRequest,
35+
KrasResponse,
36+
KrasAnswer,
37+
Dict,
38+
Headers,
39+
ScriptResponseBuilder,
40+
ScriptResponseBuilderData
41+
} from './types';
42+
export {
43+
KrasInjector,
44+
KrasConfiguration,
45+
KrasServerMethods,
46+
KrasServerHandler,
47+
KrasHandlerConfiguration,
48+
KrasRunner
49+
};
1450

15-
export type KrasRuntimeConfiguration = Partial<KrasConfiguration> & KrasHandlerConfiguration;
51+
export type KrasRuntimeConfiguration = Partial<KrasConfiguration> &
52+
KrasHandlerConfiguration;
1653

1754
export const krasrc = '.krasrc';
1855

@@ -32,7 +69,11 @@ export class MockServer extends MockServerCore implements KrasServer {
3269
super(config);
3370

3471
this.logLevel = config.logLevel || 'error';
35-
this.on('error', (e) => this.log('error', e));
72+
this.on('error', e => this.log('error', e));
73+
74+
if (config.api === false) {
75+
this.recorder.disable();
76+
}
3677

3778
withManagement(this, config);
3879
withInjectors(this, config);
@@ -41,15 +82,14 @@ export class MockServer extends MockServerCore implements KrasServer {
4182
}
4283

4384
stop() {
44-
return super.stop()
45-
.then(() => this.injectors.forEach(disposeInjector));
85+
return super.stop().then(() => this.injectors.forEach(disposeInjector));
4686
}
4787

4888
private log(type: LogEntryType, data: any) {
4989
const item: LogEntry = {
5090
type,
5191
data,
52-
time: new Date(),
92+
time: new Date()
5393
};
5494
this.logs.push(item);
5595
this.emit('logged', item);
@@ -62,7 +102,7 @@ export function readKrasConfig(options?: ConfigurationOptions, file?: string) {
62102
options,
63103
readConfiguration(homedir(), krasrc),
64104
readConfiguration(dir, krasrc),
65-
readConfiguration(dir, file),
105+
readConfiguration(dir, file)
66106
);
67107
}
68108

src/server/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export { BaseKrasServer, KrasServer, KrasServerMethods } from './kras-server';
99
export { KrasServerHandler, KrasServerHook, KrasWebSocket, KrasServerConnector } from './kras-express';
1010
export { KrasHandlerConfiguration, KrasConfigurator, KrasRunner } from './kras-handler';
1111
export { UserCredentials } from './kras-auth';
12+
export { ScriptResponseBuilder, ScriptResponseBuilderData } from './kras-script-injector';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { KrasAnswer } from './kras-injector';
2+
import { Headers } from './kras-basics';
3+
4+
export interface ScriptResponseBuilderData {
5+
statusCode: number;
6+
statusText: string;
7+
headers: Headers;
8+
content: string;
9+
}
10+
11+
export interface ScriptResponseBuilder {
12+
(data: ScriptResponseBuilderData): KrasAnswer;
13+
}

0 commit comments

Comments
 (0)