Skip to content

Commit

Permalink
adding runtime resolved gateways
Browse files Browse the repository at this point in the history
  • Loading branch information
NunuM committed Nov 5, 2023
1 parent 9d02165 commit 8e81411
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 7 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ This is a reverse proxy server that allows you to configure virtual hosts and th
### Overview

This reverse proxy server provides powerful Layer 7 (L7) routing capabilities based on virtual hosts and API gateway routes. It allows you to configure how incoming HTTP requests are routed to the appropriate upstream servers based on the requested hostname and path.
Features

### Features

#### 1. Virtual Host Routing

Expand Down
19 changes: 19 additions & 0 deletions src/app/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import appConfigurationSchema from './scema/app-config.schema.json';
import path from 'path';
import fs from "fs";
import {MiddlewareRegistry} from "../middleware/middleware-registry";
import {Header, HttpHeaders} from "pluto-http-client";
import util from "util";
import {Buffer} from "buffer";

interface Proxy {
globalMiddlewares: Array<{ type: MiddlewareRegistry, args: { [key: string]: any } }>,
Expand Down Expand Up @@ -151,6 +154,22 @@ export class Config {
return config.administration;
}

static adminAPIHeader(): Header {
if(config.administration.authenticator.type === 'none') {
return new Header(HttpHeaders.AUTHORIZATION, "none");
} else if(config.administration.authenticator.type === 'basic') {

const basicAuth = Buffer.from(
util.format("%s:%s",
config.administration.authenticator.username,
config.administration.authenticator.password)
).toString('base64');

return new Header(HttpHeaders.AUTHORIZATION, util.format("Basic %s", basicAuth));
}
return new Header(HttpHeaders.AUTHORIZATION, "none");
}

/**
* Repository to be used as persistence layer
*/
Expand Down
28 changes: 27 additions & 1 deletion src/model/api-gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import {UpstreamHost} from "./upstream-host";
import {LOGGER} from "../service/logger-service";
import {LoadBalancerFactory, LoadBalancerType} from "../load_balancer/load-balancer-type";
import {Identifiable} from "pluto-http-client/dist/framework/identifiable";
import {TimeUnit} from "pluto-http-client";
import {ClientBuilder, JsonEntity, StringEntity, TimeUnit} from "pluto-http-client";
import {ServerRequest} from "./request";
import {MiddlewareFactory, MiddlewareRegistry} from "../middleware/middleware-registry";
import {Middleware} from "../middleware/middleware";
import {Config} from "../app/config";

export class ApiGateway extends Gateway implements Identifiable {

Expand Down Expand Up @@ -234,6 +235,31 @@ export class ApiGateway extends Gateway implements Identifiable {

return undefined;
}

async addGatewayViaAdminAPI() : Promise<boolean> {
const header = Config.adminAPIHeader();

const client = new ClientBuilder()
.header(header.key, header.value)
.withTimeout(1, TimeUnit.Minutes)
.build();

const response = await client.target(`http://localhost:${Config.administration().port}`)
.path("/api/v1/api-gateway")
.request()
.post(new JsonEntity(this.toJSON()));

if (!response.getStatusInfo().getFamily().isSuccessful()) {

const entity = await response.readEntity(new StringEntity());

LOGGER.error("Error adding api gateway via admin api", this.toJSON(), response.getStatus(), entity);

return false;
} else {
return true;
}
}
}


Expand Down
3 changes: 2 additions & 1 deletion src/model/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export abstract class Gateway {

abstract get isRegexBased(): boolean;


abstract resolveUpstream(request: ServerRequest): UpstreamHost | undefined;

abstract match(domain: string): boolean;
Expand All @@ -69,6 +68,8 @@ export abstract class Gateway {

abstract toJSON(): any;

abstract addGatewayViaAdminAPI() : Promise<boolean>;

request(processor: MiddlewareProcessor, request: ServerRequest, response: ServerResponse) {

const from = processor.size;
Expand Down
32 changes: 31 additions & 1 deletion src/model/virtual-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import {LOGGER} from "../service/logger-service";
import {Gateway, GatewaysTypes} from "./gateway";
import {LoadBalancerFactory, LoadBalancerType} from "../load_balancer/load-balancer-type";
import {Identifiable} from "pluto-http-client/dist/framework/identifiable";
import {TimeUnit} from "pluto-http-client";
import {ClientBuilder, JsonEntity, LoggingFilter, StringEntity, TimeUnit} from "pluto-http-client";
import {ServerRequest} from "./request";
import {Middleware} from "../middleware/middleware";
import {MiddlewareFactory, MiddlewareRegistry} from "../middleware/middleware-registry";
import {Config} from "../app/config";


/**
Expand Down Expand Up @@ -194,6 +195,35 @@ export class VirtualHost extends Gateway implements Identifiable {

return result;
}

async addGatewayViaAdminAPI(): Promise<boolean> {

const header = Config.adminAPIHeader();

const client = new ClientBuilder()
.header(header.key, header.value)
.withTimeout(1, TimeUnit.Minutes)
.withFilter(new LoggingFilter((msg) => {
LOGGER.info(msg)
}))
.build();

const response = await client.target(`http://localhost:${Config.administration().port}`)
.path("/api/v1/virtualhost")
.request()
.post(new JsonEntity(this.toJSON()));

if (!response.getStatusInfo().getFamily().isSuccessful()) {

const entity = await response.readEntity(new StringEntity());

LOGGER.error("Error adding virtualhost via admin api", this.toJSON(), response.getStatus(), entity);

return false;
} else {
return true;
}
}
}

export interface VirtualHostObjectDefinition {
Expand Down
4 changes: 3 additions & 1 deletion src/service/gateway-host-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class GatewayHostService extends EventEmitter {
*/
addGatewayHost(gateway: Gateway) {

LOGGER.debug("adding gateway:", gateway.domain);
LOGGER.info("adding gateway:", gateway.domain);

if (gateway.isRegexBased) {

Expand Down Expand Up @@ -102,6 +102,8 @@ export class GatewayHostService extends EventEmitter {

this.addGatewayHost(gateway);

this.emit("resolved", gateway);

return gateway;
}
}
Expand Down
16 changes: 14 additions & 2 deletions src/service/proxy-starter-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {AdminApiService} from "./admin-api-service";
import {VirtualHost} from "../model/virtual-host";
import {ApiGateway} from "../model/api-gateway";
import {Repository} from "../repository/repository";
import {Gateway} from "../model/gateway";

enum Command {
Add,
Expand All @@ -35,6 +36,12 @@ export class ProxyStarterService {
.then((repository) => {
const adminApiService = new AdminApiService(repository);

gatewayHostService.on('resolved', (gateway) => {
repository.saveGateway(gateway).catch((error) => {
LOGGER.error("Error saving gateway", gateway, error)
})
});

adminApiService.on('gateway', (gateway) => {
gatewayHostService.addGatewayHost(gateway);
});
Expand Down Expand Up @@ -108,8 +115,6 @@ export class ProxyStarterService {
for (let i = 0; i < workers; i++) {
cluster.fork();
}


})
.catch((error) => {
LOGGER.error("Error connecting to repository", error);
Expand All @@ -128,6 +133,13 @@ export class ProxyStarterService {
repositoryPromise
.then((repository) => {

gatewayHostService.on('resolved', (gateway: Gateway) => {
gateway.addGatewayViaAdminAPI()
.catch((error) => {
LOGGER.error("Error adding via admin api", error);
});
});

function logError(error: Error) {
WORKER_LOGGER.log("Error saving response stats", error);
}
Expand Down

0 comments on commit 8e81411

Please sign in to comment.