Skip to content

Commit

Permalink
Merge pull request Jigsaw-Code#251 from Jigsaw-Code/fortuna-refactor
Browse files Browse the repository at this point in the history
Separate shared metrics code
  • Loading branch information
fortuna authored Sep 12, 2018
2 parents efcfc15 + 34c419d commit 499305d
Show file tree
Hide file tree
Showing 18 changed files with 962 additions and 742 deletions.
52 changes: 52 additions & 0 deletions coreos.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

51 changes: 51 additions & 0 deletions key.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
112 changes: 112 additions & 0 deletions src/shadowbox/infrastructure/json_config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2018 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import * as fs from 'fs';

import * as file_read from './file_read';
import * as logging from './logging';

export interface JsonConfig<T> {
// Returns a reference (*not* a copy) to the json object backing the config.
data(): T;
// Writes the config to the backing storage.
write();
}

export function loadFileConfig<T>(filename: string): JsonConfig<T> {
const text = file_read.readFileIfExists(filename);
let dataJson = {} as T;
if (text) {
dataJson = JSON.parse(text) as T;
}
return new FileConfig<T>(filename, dataJson);
}

// FileConfig is a JsonConfig backed by a filesystem file.
export class FileConfig<T> implements JsonConfig<T> {
constructor(private filename: string, private dataJson: T) {}

data(): T {
return this.dataJson;
}

write() {
// Write to temporary file, then move that temporary file to the
// persistent location, to avoid accidentally breaking the metrics file.
// Use *Sync calls for atomic operations, to guard against corrupting
// these files.
const tempFilename = `${this.filename}.${Date.now()}`;
try {
fs.writeFileSync(tempFilename, JSON.stringify(this.dataJson), {encoding: 'utf8'});
fs.renameSync(tempFilename, this.filename);
} catch (error) {
// TODO: Stop swalling the exception and handle it in the callers.
logging.error(`Error writing config ${this.filename} ${error}`);
}
}
}

// ChildConfig is a JsonConfig backed by another config.
export class ChildConfig<T> implements JsonConfig<T> {
constructor(private parentConfig: JsonConfig<{}>, private dataJson: T) {}

data(): T {
return this.dataJson;
}

write() {
this.parentConfig.write();
}
}

// DelayedConfig is a JsonConfig that only writes the data in a periodic time interval.
// Calls to write() will mark the data as "dirty" for the next inverval.
export class DelayedConfig<T> implements JsonConfig<T> {
private dirty = false;
constructor(private config: JsonConfig<T>, writePeriodMs: number) {
// This repeated call will never be cancelled until the execution is terminated.
setInterval(() => {
if (!this.dirty) {
return;
}
this.config.write();
this.dirty = false;
}, writePeriodMs);
}

data(): T {
return this.config.data();
}

write() {
this.dirty = true;
}
}

// InMemoryConfig is a JsonConfig backed by an internal member variable. Useful for testing.
export class InMemoryConfig<T> implements JsonConfig<T> {
// Holds the data JSON as it was when `write()` was called.
public mostRecentWrite: T;
constructor(private dataJson: T) {
this.mostRecentWrite = this.dataJson;
}

data(): T {
return this.dataJson;
}

write() {
this.mostRecentWrite = JSON.parse(JSON.stringify(this.dataJson));
}
}
27 changes: 4 additions & 23 deletions src/shadowbox/model/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,15 @@
import {AccessKeyId} from './access_key';

export type LastHourMetricsReadyCallback =
(startDatetime: Date, endDatetime: Date, lastHourUserStats: Map<AccessKeyId, PerUserStats>) =>
void;
(startDatetime: Date, endDatetime: Date,
lastHourUserMetrics: Map<AccessKeyId, PerUserMetrics>) => void;

// TODO: replace "user" with "access key" in metrics. This may also require changing
// - the metrics server
// - the metrics bigquery tables
// - the persisted metrics format (JSON file).
export interface Stats {
// Record the number of bytes transferred for a user, and include known
// client IP addresses that are connected for that user. If there are >1
// IP addresses, numBytes is the sum of bytes transferred across all of those
// clients - we do not know the breakdown of how many bytes were transferred
// per IP address, due to limitations of the ss-server. ipAddresses are only
// used for recording which countries clients are connecting from.
recordBytesTransferred(
userId: AccessKeyId, metricsUserId: AccessKeyId, numBytes: number, ipAddresses: string[]);
// Get 30 day data usage, broken down by userId.
get30DayByteTransfer(): DataUsageByUser;
// Register callback for hourly metrics report.
onLastHourMetricsReady(callback: LastHourMetricsReadyCallback): void;
}

export interface PerUserStats {
export interface PerUserMetrics {
bytesTransferred: number;
anonymizedIpAddresses: Set<string>;
}

// Byte transfer stats for the past 30 days, including both inbound and outbound.
// Byte transfer metrics for the past 30 days, including both inbound and outbound.
// TODO: this is copied at src/model/server.ts. Both copies should
// be kept in sync, until we can find a way to share code between the web_app
// and shadowbox.
Expand Down
2 changes: 1 addition & 1 deletion src/shadowbox/model/shadowsocks_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import * as dgram from 'dgram';

export interface ShadowsocksServer {
startInstance(
portNumber: number, password: string, statsSocket: dgram.Socket,
portNumber: number, password: string, metricsSocket: dgram.Socket,
encryptionMethod?: string): Promise<ShadowsocksInstance>;
}

Expand Down
34 changes: 17 additions & 17 deletions src/shadowbox/server/libev_shadowsocks_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ export class LibevShadowsocksServer implements ShadowsocksServer {
constructor(private publicAddress: string, private verbose: boolean) {}

public startInstance(
portNumber: number, password: string, statsSocket: dgram.Socket,
portNumber: number, password: string, metricsSocket: dgram.Socket,
encryptionMethod = this.DEFAULT_METHOD): Promise<ShadowsocksInstance> {
logging.info(`Starting server on port ${portNumber}`);

const statsAddress = statsSocket.address();
const metricsAddress = metricsSocket.address();
const commandArguments = [
'-m', encryptionMethod, // Encryption method
'-u', // Allow UDP
'--fast-open', // Allow TCP fast open
'-p', portNumber.toString(), '-k', password, '--manager-address',
`${statsAddress.address}:${statsAddress.port}`
`${metricsAddress.address}:${metricsAddress.port}`
];
logging.info('starting ss-server with args: ' + commandArguments.join(' '));
// Add the system DNS servers.
Expand Down Expand Up @@ -77,7 +77,7 @@ export class LibevShadowsocksServer implements ShadowsocksServer {
}));

return Promise.resolve(new LibevShadowsocksServerInstance(
childProcess, portNumber, password, encryptionMethod, accessUrl, statsSocket));
childProcess, portNumber, password, encryptionMethod, accessUrl, metricsSocket));
}
}

Expand All @@ -88,7 +88,7 @@ class LibevShadowsocksServerInstance implements ShadowsocksInstance {
constructor(
private childProcess: child_process.ChildProcess, public portNumber: number, public password,
public encryptionMethod: string, public accessUrl: string,
private statsSocket: dgram.Socket) {}
private metricsSocket: dgram.Socket) {}

public stop() {
logging.info(`Stopping server on port ${this.portNumber}`);
Expand All @@ -106,30 +106,30 @@ class LibevShadowsocksServerInstance implements ShadowsocksInstance {
// https://github.com/shadowsocks/shadowsocks-libev/blob/a16826b83e73af386806d1b51149f8321820835e/src/server.c#L172
public onInboundBytes(callback: (bytes: number, ipAddresses: string[]) => void) {
if (this.eventEmitter.listenerCount(this.INBOUND_BYTES_EVENT) === 0) {
this.createStatsListener();
this.createMetricsListener();
}
this.eventEmitter.on(this.INBOUND_BYTES_EVENT, callback);
}

private createStatsListener() {
private createMetricsListener() {
let lastInboundBytes = 0;
this.statsSocket.on('message', (buf: Buffer) => {
let statsMessage;
this.metricsSocket.on('message', (buf: Buffer) => {
let metricsMessage;
try {
statsMessage = parseStatsMessage(buf);
metricsMessage = parseMetricsMessage(buf);
} catch (err) {
logging.error('error parsing stats: ' + buf + ', ' + err);
logging.error('error parsing metrics: ' + buf + ', ' + err);
return;
}
if (statsMessage.portNumber !== this.portNumber) {
// Ignore stats for other ss-servers, which post to the same statsSocket.
if (metricsMessage.portNumber !== this.portNumber) {
// Ignore metrics for other ss-servers, which post to the same metricsSocket.
return;
}
const delta = statsMessage.totalInboundBytes - lastInboundBytes;
const delta = metricsMessage.totalInboundBytes - lastInboundBytes;
if (delta > 0) {
this.getConnectedClientIPAddresses()
.then((ipAddresses: string[]) => {
lastInboundBytes = statsMessage.totalInboundBytes;
lastInboundBytes = metricsMessage.totalInboundBytes;
this.eventEmitter.emit(this.INBOUND_BYTES_EVENT, delta, ipAddresses);
})
.catch((err) => {
Expand Down Expand Up @@ -165,12 +165,12 @@ class LibevShadowsocksServerInstance implements ShadowsocksInstance {
}
}

interface StatsMessage {
interface MetricsMessage {
portNumber: number;
totalInboundBytes: number;
}

function parseStatsMessage(buf): StatsMessage {
function parseMetricsMessage(buf): MetricsMessage {
const jsonString = buf.toString()
.substr('stat: '.length) // remove leading "stat: "
.replace(/\0/g, ''); // remove trailing null terminator
Expand Down
Loading

0 comments on commit 499305d

Please sign in to comment.