Replaced prologue implementation with mummy for listener management, since it seems more suitable for future use (websockets, etc.).
This commit is contained in:
@@ -25,8 +25,8 @@ requires "argparse >= 4.0.2"
|
||||
requires "parsetoml >= 0.7.2"
|
||||
requires "nimcrypto >= 0.6.4"
|
||||
requires "tiny_sqlite >= 0.2.0"
|
||||
requires "prologue >= 0.6.6"
|
||||
requires "winim >= 3.9.4"
|
||||
requires "ptr_math >= 0.3.0"
|
||||
requires "imguin >= 1.92.2.1"
|
||||
requires "zippy >= 0.10.16"
|
||||
requires "zippy >= 0.10.16"
|
||||
requires "mummy >= 0.4.6"
|
||||
@@ -3,6 +3,6 @@
|
||||
-d:release
|
||||
--opt:size
|
||||
--passL:"-s" # Strip symbols, such as sensitive function names
|
||||
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
|
||||
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
|
||||
-d:MODULES=1
|
||||
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"
|
||||
@@ -1,24 +1,24 @@
|
||||
[Window][Sessions [Table View]]
|
||||
Pos=10,43
|
||||
Size=1533,389
|
||||
Size=1533,946
|
||||
Collapsed=0
|
||||
DockId=0x00000003,0
|
||||
|
||||
[Window][Listeners]
|
||||
Pos=10,43
|
||||
Size=1533,389
|
||||
Size=1533,946
|
||||
Collapsed=0
|
||||
DockId=0x00000003,1
|
||||
|
||||
[Window][Eventlog]
|
||||
Pos=1545,43
|
||||
Size=353,389
|
||||
Size=353,946
|
||||
Collapsed=0
|
||||
DockId=0x00000004,0
|
||||
|
||||
[Window][Dear ImGui Demo]
|
||||
Pos=1545,43
|
||||
Size=353,389
|
||||
Size=353,946
|
||||
Collapsed=0
|
||||
DockId=0x00000004,1
|
||||
|
||||
@@ -139,6 +139,6 @@ DockNode ID=0x00000009 Pos=100,200 Size=754,103 Selected=0x64D005CF
|
||||
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=Y
|
||||
DockNode ID=0x00000005 Parent=0x85940918 SizeRef=1888,389 Split=X
|
||||
DockNode ID=0x00000003 Parent=0x00000005 SizeRef=1533,159 CentralNode=1 Selected=0x61E02D75
|
||||
DockNode ID=0x00000004 Parent=0x00000005 SizeRef=353,159 Selected=0x0FA43D88
|
||||
DockNode ID=0x00000004 Parent=0x00000005 SizeRef=353,159 Selected=0x5E5F7166
|
||||
DockNode ID=0x00000006 Parent=0x85940918 SizeRef=1888,555 Selected=0x65D642C0
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import prompt
|
||||
import tables
|
||||
import times
|
||||
import parsetoml
|
||||
import mummy
|
||||
|
||||
# Custom Binary Task structure
|
||||
const
|
||||
@@ -196,6 +197,7 @@ type
|
||||
HTTP = "http"
|
||||
|
||||
Listener* = ref object of RootObj
|
||||
server*: Server
|
||||
listenerId*: string
|
||||
address*: string
|
||||
port*: int
|
||||
@@ -212,7 +214,7 @@ type
|
||||
Conquest* = ref object
|
||||
prompt*: Prompt
|
||||
dbPath*: string
|
||||
listeners*: Table[string, Listener]
|
||||
listeners*: Table[string, tuple[listener: Listener, thread: Thread[Listener]]]
|
||||
agents*: Table[string, Agent]
|
||||
interactAgent*: Agent
|
||||
keyPair*: KeyPair
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import prologue, terminal, strformat, parsetoml, tables
|
||||
import mummy, terminal, strformat, parsetoml, tables
|
||||
import strutils, base64
|
||||
|
||||
import ./handlers
|
||||
@@ -6,15 +6,32 @@ import ../globals
|
||||
import ../core/logger
|
||||
import ../../common/[types, utils, serialize, profile]
|
||||
|
||||
proc error404*(ctx: Context) {.async.} =
|
||||
resp "", Http404
|
||||
# Not Found
|
||||
proc error404*(request: Request) =
|
||||
request.respond(404, body = "")
|
||||
|
||||
# Method not allowed
|
||||
proc error405*(request: Request) =
|
||||
request.respond(404, body = "")
|
||||
|
||||
# Utils
|
||||
proc hasKey(headers: seq[(string, string)], headerName: string): bool =
|
||||
for (name, value) in headers:
|
||||
if name.toLower() == headerName.toLower():
|
||||
return true
|
||||
return false
|
||||
|
||||
proc get(headers: seq[(string, string)], headerName: string): string =
|
||||
for (name, value) in headers:
|
||||
if name.toLower() == headerName.toLower():
|
||||
return value
|
||||
return ""
|
||||
|
||||
#[
|
||||
GET
|
||||
Called from agent to check for new tasks
|
||||
]#
|
||||
proc httpGet*(ctx: Context) {.async.} =
|
||||
|
||||
proc httpGet*(request: Request) =
|
||||
{.cast(gcsafe).}:
|
||||
|
||||
# Check heartbeat metadata placement
|
||||
@@ -24,17 +41,16 @@ proc httpGet*(ctx: Context) {.async.} =
|
||||
case cq.profile.getString("http-get.agent.heartbeat.placement.type"):
|
||||
of "header":
|
||||
let heartbeatHeader = cq.profile.getString("http-get.agent.heartbeat.placement.name")
|
||||
if not ctx.request.hasHeader(heartbeatHeader):
|
||||
resp "", Http404
|
||||
if not request.headers.hasKey(heartbeatHeader):
|
||||
request.respond(404, body = "")
|
||||
return
|
||||
|
||||
heartbeatString = ctx.request.getHeader(heartbeatHeader)[0]
|
||||
heartbeatString = request.headers.get(heartbeatHeader)
|
||||
|
||||
of "parameter":
|
||||
let param = cq.profile.getString("http-get.agent.heartbeat.placement.name")
|
||||
heartbeatString = ctx.getQueryParams(param)
|
||||
heartbeatString = request.queryParams.get(param)
|
||||
if heartbeatString.len <= 0:
|
||||
resp "", Http404
|
||||
request.respond(404, body = "")
|
||||
return
|
||||
|
||||
of "uri":
|
||||
@@ -60,7 +76,7 @@ proc httpGet*(ctx: Context) {.async.} =
|
||||
let tasks: seq[seq[byte]] = getTasks(heartbeat)
|
||||
|
||||
if tasks.len <= 0:
|
||||
resp "", Http200
|
||||
request.respond(200, body = "")
|
||||
return
|
||||
|
||||
# Create response, containing number of tasks, as well as length and content of each task
|
||||
@@ -73,7 +89,6 @@ proc httpGet*(ctx: Context) {.async.} =
|
||||
|
||||
# Apply data transformation to the response
|
||||
var response: string
|
||||
|
||||
case cq.profile.getString("http-get.server.output.encoding.type", default = "none"):
|
||||
of "none":
|
||||
response = Bytes.toString(responseBytes)
|
||||
@@ -85,52 +100,52 @@ proc httpGet*(ctx: Context) {.async.} =
|
||||
let suffix = cq.profile.getString("http-get.server.output.suffix")
|
||||
|
||||
# Add headers, as defined in the team server profile
|
||||
var headers: HttpHeaders
|
||||
for header, value in cq.profile.getTable("http-get.server.headers"):
|
||||
ctx.response.setHeader(header, value.getStringValue())
|
||||
headers.add((header, value.getStringValue()))
|
||||
|
||||
await ctx.respond(Http200, prefix & response & suffix, ctx.response.headers)
|
||||
ctx.handled = true # Ensure that HTTP response is sent only once
|
||||
request.respond(200, headers = headers, body = prefix & response & suffix)
|
||||
|
||||
# Notify operator that agent collected tasks
|
||||
cq.info(fmt"{$response.len} bytes sent.")
|
||||
|
||||
except CatchableError:
|
||||
resp "", Http404
|
||||
request.respond(404, body = "")
|
||||
|
||||
#[
|
||||
POST
|
||||
Called from agent to register itself or post results of a task
|
||||
]#
|
||||
proc httpPost*(ctx: Context) {.async.} =
|
||||
|
||||
proc httpPost*(request: Request) =
|
||||
{.cast(gcsafe).}:
|
||||
|
||||
# Check headers
|
||||
# If POST data is not binary data, return 404 error code
|
||||
if ctx.request.contentType != "application/octet-stream":
|
||||
resp "", Http404
|
||||
if request.headers.get("Content-Type") != "application/octet-stream":
|
||||
request.respond(404, body = "")
|
||||
return
|
||||
|
||||
try:
|
||||
# Differentiate between registration and task result packet
|
||||
var unpacker = Unpacker.init(ctx.request.body)
|
||||
var unpacker = Unpacker.init(request.body)
|
||||
let header = unpacker.deserializeHeader()
|
||||
|
||||
# Add response headers, as defined in team server profile
|
||||
var headers: HttpHeaders
|
||||
for header, value in cq.profile.getTable("http-post.server.headers"):
|
||||
ctx.response.setHeader(header, value.getStringValue())
|
||||
headers.add((header, value.getStringValue()))
|
||||
|
||||
if cast[PacketType](header.packetType) == MSG_REGISTER:
|
||||
if not register(string.toBytes(ctx.request.body)):
|
||||
resp "", Http400
|
||||
if not register(string.toBytes(request.body)):
|
||||
request.respond(400, body = "")
|
||||
return
|
||||
|
||||
elif cast[PacketType](header.packetType) == MSG_RESULT:
|
||||
handleResult(string.toBytes(ctx.request.body))
|
||||
handleResult(string.toBytes(request.body))
|
||||
|
||||
resp "", Http200
|
||||
request.respond(200, body = "")
|
||||
|
||||
except CatchableError:
|
||||
resp "", Http404
|
||||
request.respond(404, body = "")
|
||||
|
||||
return
|
||||
@@ -141,7 +141,7 @@ proc agentBuild*(cq: Conquest, listener, sleepDelay: string, sleepTechnique: str
|
||||
cq.error(fmt"Listener {listener.toUpperAscii} does not exist.")
|
||||
return false
|
||||
|
||||
let listener = cq.listeners[listener.toUpperAscii]
|
||||
let listener = cq.listeners[listener.toUpperAscii].listener
|
||||
|
||||
var config: seq[byte]
|
||||
if sleepDelay.isEmptyOrWhitespace():
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import strformat, strutils, terminal
|
||||
import prologue, parsetoml
|
||||
import mummy, mummy/routers
|
||||
import parsetoml
|
||||
|
||||
import ../globals
|
||||
import ../utils
|
||||
import ../api/routes
|
||||
import ../db/database
|
||||
import ../core/logger
|
||||
import ../../common/[types, utils, profile]
|
||||
|
||||
# Utility functions
|
||||
proc delListener(cq: Conquest, listenerName: string) =
|
||||
cq.listeners.del(listenerName)
|
||||
|
||||
proc add(cq: Conquest, listener: Listener) =
|
||||
cq.listeners[listener.listenerId] = listener
|
||||
|
||||
#[
|
||||
Listener management
|
||||
]#
|
||||
@@ -36,105 +31,111 @@ proc listenerList*(cq: Conquest) =
|
||||
let listeners = cq.dbGetAllListeners()
|
||||
cq.drawTable(listeners)
|
||||
|
||||
proc serve(listener: Listener) {.thread.} =
|
||||
try:
|
||||
listener.server.serve(Port(listener.port), listener.address)
|
||||
except Exception:
|
||||
discard
|
||||
|
||||
proc listenerStart*(cq: Conquest, host: string, portStr: string) =
|
||||
|
||||
# Validate arguments
|
||||
try:
|
||||
if not validatePort(portStr):
|
||||
raise newException(CatchableError,fmt"[ - ] Invalid port number: {portStr}")
|
||||
raise newException(CatchableError, fmt"[ - ] Invalid port number: {portStr}")
|
||||
|
||||
let port = portStr.parseInt
|
||||
|
||||
# Create new listener
|
||||
let
|
||||
name: string = generateUUID()
|
||||
listenerSettings = newSettings(
|
||||
appName = name,
|
||||
debug = false,
|
||||
address = "", # For some reason, the program crashes when the ip parameter is passed to the newSettings function
|
||||
port = Port(port) # As a result, I will hardcode the listener to be served on all interfaces (0.0.0.0) by default
|
||||
) # TODO: fix this issue and start the listener on the address passed as the HOST parameter
|
||||
let name: string = generateUUID()
|
||||
var router: Router
|
||||
router.notFoundHandler = routes.error404
|
||||
router.methodNotAllowedHandler = routes.error405
|
||||
|
||||
var listener = newApp(settings = listenerSettings)
|
||||
|
||||
# Define API endpoints based on C2 profile
|
||||
# GET requests
|
||||
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
||||
listener.addRoute(endpoint.getStringValue(), routes.httpGet)
|
||||
router.addRoute("GET", endpoint.getStringValue(), routes.httpGet)
|
||||
|
||||
# POST requests
|
||||
var postMethods: seq[HttpMethod]
|
||||
var postMethods: seq[string]
|
||||
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
||||
postMethods.add(parseEnum[HttpMethod](reqMethod.getStringValue()))
|
||||
postMethods.add(reqMethod.getStringValue())
|
||||
|
||||
# Default method is POST
|
||||
if postMethods.len == 0:
|
||||
postMethods = @[HttpPost]
|
||||
postMethods = @["POST"]
|
||||
|
||||
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
||||
listener.addRoute(endpoint.getStringValue(), routes.httpPost, postMethods)
|
||||
for httpMethod in postMethods:
|
||||
router.addRoute(httpMethod, endpoint.getStringValue(), routes.httpPost)
|
||||
|
||||
listener.registerErrorHandler(Http404, routes.error404)
|
||||
let server = newServer(router.toHandler())
|
||||
|
||||
# Store listener in database
|
||||
var listenerInstance = Listener(
|
||||
var listener = Listener(
|
||||
server: server,
|
||||
listenerId: name,
|
||||
address: host,
|
||||
port: port,
|
||||
protocol: HTTP
|
||||
)
|
||||
if not cq.dbStoreListener(listenerInstance):
|
||||
raise newException(CatchableError, "Failed to store listener in database.")
|
||||
|
||||
# Start serving
|
||||
discard listener.runAsync()
|
||||
cq.add(listenerInstance)
|
||||
var thread: Thread[Listener]
|
||||
createThread(thread, serve, listener)
|
||||
server.waitUntilReady()
|
||||
|
||||
cq.listeners[name] = (listener, thread)
|
||||
if not cq.dbStoreListener(listener):
|
||||
raise newException(CatchableError, "Failed to store listener in database.")
|
||||
|
||||
cq.success("Started listener", fgGreen, fmt" {name} ", resetStyle, fmt"on {host}:{portStr}.")
|
||||
|
||||
except CatchableError as err:
|
||||
cq.error("Failed to start listener: ", err.msg)
|
||||
|
||||
proc restartListeners*(cq: Conquest) =
|
||||
let listeners: seq[Listener] = cq.dbGetAllListeners()
|
||||
proc restartListeners*(cq: Conquest) =
|
||||
var listeners: seq[Listener] = cq.dbGetAllListeners()
|
||||
|
||||
# Restart all active listeners that are stored in the database
|
||||
for l in listeners:
|
||||
for listener in listeners:
|
||||
try:
|
||||
let
|
||||
settings = newSettings(
|
||||
appName = l.listenerId,
|
||||
debug = false,
|
||||
address = "",
|
||||
port = Port(l.port)
|
||||
)
|
||||
listener = newApp(settings = settings)
|
||||
|
||||
# Create new listener
|
||||
let name: string = generateUUID()
|
||||
var router: Router
|
||||
router.notFoundHandler = routes.error404
|
||||
router.methodNotAllowedHandler = routes.error405
|
||||
|
||||
# Define API endpoints based on C2 profile
|
||||
# GET requests
|
||||
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
||||
listener.get(endpoint.getStringValue(), routes.httpGet)
|
||||
router.addRoute("GET", endpoint.getStringValue(), routes.httpGet)
|
||||
|
||||
# POST requests
|
||||
var postMethods: seq[HttpMethod]
|
||||
var postMethods: seq[string]
|
||||
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
||||
postMethods.add(parseEnum[HttpMethod](reqMethod.getStringValue()))
|
||||
postMethods.add(reqMethod.getStringValue())
|
||||
|
||||
# Default method is POST
|
||||
if postMethods.len == 0:
|
||||
postMethods = @[HttpPost]
|
||||
postMethods = @["POST"]
|
||||
|
||||
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
||||
listener.addRoute(endpoint.getStringValue(), routes.httpPost, postMethods)
|
||||
|
||||
listener.registerErrorHandler(Http404, routes.error404)
|
||||
for httpMethod in postMethods:
|
||||
router.addRoute(httpMethod, endpoint.getStringValue(), routes.httpPost)
|
||||
|
||||
discard listener.runAsync()
|
||||
cq.add(l)
|
||||
cq.success("Restarted listener", fgGreen, fmt" {l.listenerId} ", resetStyle, fmt"on {l.address}:{$l.port}.")
|
||||
|
||||
# Delay before serving another listener to avoid crashing the application
|
||||
waitFor sleepAsync(100)
|
||||
|
||||
let server = newServer(router.toHandler())
|
||||
listener.server = server
|
||||
|
||||
# Start serving
|
||||
var thread: Thread[Listener]
|
||||
createThread(thread, serve, listener)
|
||||
server.waitUntilReady()
|
||||
|
||||
cq.listeners[listener.listenerId] = (listener, thread)
|
||||
cq.success("Restarted listener", fgGreen, fmt" {listener.listenerId} ", resetStyle, fmt"on {listener.address}:{$listener.port}.")
|
||||
|
||||
except CatchableError as err:
|
||||
cq.error("Failed to restart listener: ", err.msg)
|
||||
|
||||
@@ -154,6 +155,14 @@ proc listenerStop*(cq: Conquest, name: string) =
|
||||
cq.error("Failed to stop listener: ", getCurrentExceptionMsg())
|
||||
return
|
||||
|
||||
cq.delListener(name)
|
||||
cq.listeners.del(name)
|
||||
cq.success("Stopped listener ", fgGreen, name.toUpperAscii, resetStyle, ".")
|
||||
|
||||
# TODO: Make listener stoppable
|
||||
# try:
|
||||
# cq.listeners[name].listener .server.close()
|
||||
# joinThread(cq.listeners[name].thread)
|
||||
# except:
|
||||
# cq.error("Failed to stop listener.")
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ proc header() =
|
||||
proc init*(T: type Conquest, profile: Profile): Conquest =
|
||||
var cq = new Conquest
|
||||
cq.prompt = Prompt.init()
|
||||
cq.listeners = initTable[string, Listener]()
|
||||
cq.listeners = initTable[string, tuple[listener: Listener, thread: Thread[Listener]]]()
|
||||
cq.agents = initTable[string, Agent]()
|
||||
cq.interactAgent = nil
|
||||
cq.profile = profile
|
||||
|
||||
Reference in New Issue
Block a user