Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ ingestors/java/spring/.idea/
ingestors/java/spring/gradle.properties
ingestors/java/spring/target/

ingestors/java/spring-jakarta/.gradle/
ingestors/java/spring-jakarta/.idea/
ingestors/java/spring-jakarta/gradle.properties
ingestors/java/spring-jakarta/target/
ingestors/java/spring-jakarta/out/

.meta/*
ingestors/govxlan/govxlan
testing/tmp_tests
165 changes: 165 additions & 0 deletions ingestors/java/spring-jakarta/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<build>
<plugins>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.13</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
</transformer>
</transformers>
<createDependencyReducedPom>
true
</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>

<name>spring</name>
<url>https://www.github.com/metlo-labs/metlo</url>
<description>Java Agent for Metlo</description>
<licenses>
<license>
<name>MIT License</name>
<url>http://www.opensource.org/licenses/mit-license.php</url>
</license>
</licenses>
<developers>
<developer>
<name>Ninad Sinha</name>
<email>ninad@metlo.com</email>
<organization>com.metlo</organization>
<organizationUrl>https://www.metlo.com</organizationUrl>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/metlo-labs/metlo.git</connection>
<developerConnection>scm:git:ssh://github.com:metlo-labs/metlo.git</developerConnection>
<url>http://github.com/metlo-labs/metlo</url>
</scm>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>

</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/content/repositories/releases/</url>
</repository>
</distributionManagement>

<groupId>com.metlo</groupId>
<artifactId>spring-jakarta</artifactId>
<version>0.0.1</version>

<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
<scope>compile</scope>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.0.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>9.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package com.metlo.spring;

import com.metlo.spring.utils.ContentCachingResponseWrapperWithHeaderNames;
import com.metlo.spring.utils.RateLimitingRequests;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.function.Function;


public class Metlo extends OncePerRequestFilter {
private static final int DEFAULT_THREAD_POOL_SIZE = 2;

private static final int DEFAULT_RPS = 10;
private final static String endpoint = "api/v1/log-request/single";

private final Boolean enabled;

private final RateLimitingRequests req;

public Metlo(String host, String api_key) {
this(DEFAULT_THREAD_POOL_SIZE, host, api_key, DEFAULT_RPS);
}

public Metlo(String host, String api_key, Integer rps) {
this(DEFAULT_THREAD_POOL_SIZE, host, api_key, rps);
}

public Metlo(int pool_size, String host, String api_key, Integer rps) {

String METLO_ADDR = host;
if (host.charAt(host.length() - 1) == '/') {
METLO_ADDR += endpoint;
} else {
METLO_ADDR += "/" + endpoint;
}
this.req = new RateLimitingRequests(rps, pool_size, METLO_ADDR, api_key);

String enabled = System.getenv("METLO_ENABLED");
if (enabled != null) {
this.enabled = Boolean.parseBoolean(enabled);
} else {
this.enabled = true;
}
}


@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapperWithHeaderNames responseWrapper = new ContentCachingResponseWrapperWithHeaderNames(response);

filterChain.doFilter(requestWrapper, responseWrapper);
if (this.enabled) {
String requestBody = getStringValue(requestWrapper.getContentAsByteArray(),
request.getCharacterEncoding());
String responseBody = getStringValue(responseWrapper.getContentAsByteArray(),
response.getCharacterEncoding());

this.req.send(createDataBinding(requestWrapper, responseWrapper, requestBody, responseBody));
// copyBodyToResponse requires min Spring 4.2.0
responseWrapper.copyBodyToResponse();
}
}

private String getStringValue(byte[] contentAsByteArray, String characterEncoding) {
try {
return new String(contentAsByteArray, 0, contentAsByteArray.length, characterEncoding);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "";
}

private Map<String, Object> createDataBinding(HttpServletRequest request, ContentCachingResponseWrapperWithHeaderNames response, String request_body, String response_body) {
Map<String, Object> DATA = new HashMap<String, Object>();
Map<String, Object> REQUEST = new HashMap<>();
Map<String, Object> REQUEST_URL = new HashMap<>();
Map<String, Object> RESPONSE = new HashMap<>();
Map<String, Object> META = new HashMap<>();

String host = request.getHeader("host");
if (host == null) {
host = request.getRemoteHost();
}
REQUEST_URL.put("host", host);
REQUEST_URL.put("path", request.getRequestURI());
REQUEST_URL.put("parameters", listParamFormat(request.getParameterMap()));

REQUEST.put("url", REQUEST_URL);
try {
REQUEST.put("headers", listRequestHeaderFormat(request.getHeaderNames(), request::getHeader));
} catch (Exception e) {
REQUEST.put("headers", new ArrayList<Map<String, String>>());
}
REQUEST.put("body", request_body);
REQUEST.put("method", request.getMethod());

RESPONSE.put("url", request.getLocalAddr());
RESPONSE.put("status", response.getStatus());
try {
RESPONSE.put("headers", listResponseHeaderFormat(response.getHeaderNames(), response::getHeader));
} catch (Exception e) {
RESPONSE.put("headers", new ArrayList<Map<String, String>>());
}
boolean foundContentType = false;
for (Map<String, String> header : (ArrayList<Map<String, String>>) RESPONSE.get("headers")) {
if (header.get("name").equals("Content-Type")) {
header.put("value", response.getResponse().getContentType());
foundContentType = true;
break;
}
}
if (!foundContentType) {
Map<String, String> entry = new HashMap<String, String>();
entry.put("name", "Content-Type");
entry.put("value", response.getResponse().getContentType());
((ArrayList<Map<String, String>>) RESPONSE.get("headers")).add(entry);
}


RESPONSE.put("body", response_body);

META.put("source", request.getRemoteAddr());
META.put("sourcePort", request.getRemotePort());
META.put("destination", request.getLocalAddr());
META.put("destinationPort", request.getLocalPort());
META.put("environment", "production");
META.put("incoming", "true");
META.put("metloSource", "java/spring");

DATA.put("request", REQUEST);
DATA.put("response", RESPONSE);
DATA.put("meta", META);

return DATA;
}

private List<Map<String, String>> listParamFormat(Map<String, String[]> map) {
List<Map<String, String>> _formatted_params_ = new ArrayList<>();
for (Map.Entry<String, String[]> entry_raw : map.entrySet()) {
HashMap<String, String> entry = new HashMap<String, String>();
entry.put("name", entry_raw.getKey());
entry.put("value", "[" + String.join(",", entry_raw.getValue()) + "]");
_formatted_params_.add(entry);
}
return _formatted_params_;
}

private List<Map<String, String>> listRequestHeaderFormat(Enumeration<String> headerNames, Function<String, String> headerValueForName) throws Exception {
List<Map<String, String>> headers = new ArrayList<Map<String, String>>();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = headerValueForName.apply(headerName);
Map<String, String> individualHeader = new HashMap<String, String>();
individualHeader.put("name", headerName);
individualHeader.put("value", headerValue);
headers.add(individualHeader);
}
return headers;
}

private List<Map<String, String>> listResponseHeaderFormat(Collection<String> headerNames, Function<String, String> headerValueForName) throws Exception {
List<Map<String, String>> headers = new ArrayList<Map<String, String>>();
for (String headerName : headerNames) {
String headerValue = headerValueForName.apply(headerName);
Map<String, String> individualHeader = new HashMap<String, String>();
individualHeader.put("name", headerName);
individualHeader.put("value", headerValue);
headers.add(individualHeader);
}
return headers;
}

}
Loading