Skip to content

Commit

Permalink
Merge branch 'master' into openapi-wildcard-700
Browse files Browse the repository at this point in the history
  • Loading branch information
predic8 authored Aug 23, 2023
2 parents 5fc53a9 + c70ac9e commit 6a6bd6f
Show file tree
Hide file tree
Showing 12 changed files with 293 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ public ResponseBuilder contentType(String type) {
return this;
}

public ResponseBuilder yaml() {
res.getHeader().setContentType(APPLICATION_X_YAML);
return this;
}

public ResponseBuilder location(String location) {
res.getHeader().setLocation(location);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@

package com.predic8.membrane.core.interceptor.ratelimit;

import javax.annotation.concurrent.GuardedBy;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class LazyRateLimit extends RateLimitStrategy {

private final Object lock = new Object();
@GuardedBy("lock")
private java.time.LocalDateTime nextCleanup = LocalDateTime.now();
public ConcurrentHashMap<String, AtomicInteger> requestCounterFromKey = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, AtomicInteger> requestCounterFromKey = new ConcurrentHashMap<>();

public LazyRateLimit(java.time.Duration requestLimitDuration, int requestLimit) {
this.requestLimitDuration = requestLimitDuration;
Expand All @@ -31,29 +34,26 @@ public LazyRateLimit(java.time.Duration requestLimitDuration, int requestLimit)

@Override
public boolean isRequestLimitReached(String key) {
synchronized (nextCleanup) {
synchronized (lock) {
if (java.time.LocalDateTime.now().isAfter(nextCleanup)) {
for (AtomicInteger info : requestCounterFromKey.values()) {
info.set(0);
}
requestCounterFromKey.clear();
incrementNextCleanupTime();
}
}

addRequestEntry(key);
return requestCounterFromKey.get(key).get() > requestLimit;
}

private void addRequestEntry(String addr) {
synchronized (requestCounterFromKey) {
if (!requestCounterFromKey.containsKey(addr)) {
requestCounterFromKey.put(addr, new AtomicInteger());
}
}
requestCounterFromKey.computeIfAbsent(addr, s -> new AtomicInteger());
requestCounterFromKey.get(addr).incrementAndGet();
}

private void incrementNextCleanupTime() {
nextCleanup = java.time.LocalDateTime.now().plus(requestLimitDuration);
synchronized (lock) {
nextCleanup = java.time.LocalDateTime.now().plus(requestLimitDuration);
}
}

@Override
Expand All @@ -63,9 +63,7 @@ public LocalDateTime getServiceAvailableAgainTime(String key) {

@Override
public void updateAfterConfigChange() {
for (AtomicInteger info : requestCounterFromKey.values()) {
info.set(0);
}
requestCounterFromKey.clear();
incrementNextCleanupTime();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
* The X-Forwarded-For header can only be trusted when a trustworthy reverse proxy or load balancer is between the client and server. The gateway not should be
* reachable directly. Only activate this feature when you know what you are doing.
* </p>
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For">X-Forwarded-For @Mozilla</a>
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For">X-Forwarded-For &#64;Mozilla</a>
*/
@MCElement(name = "rateLimiter")
public class RateLimitInterceptor extends AbstractInterceptor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

import static com.predic8.membrane.core.exceptions.ProblemDetails.*;
import static com.predic8.membrane.core.http.MimeType.*;
import static com.predic8.membrane.core.http.Response.*;
import static com.predic8.membrane.core.interceptor.Outcome.*;
import static com.predic8.membrane.core.openapi.util.OpenAPIUtil.*;
import static com.predic8.membrane.core.openapi.util.Utils.*;
Expand Down Expand Up @@ -103,12 +104,12 @@ private Outcome handleOverviewOpenAPIDoc(Exchange exc) throws IOException, URISy
}

private Outcome returnJsonOverview(Exchange exc) throws JsonProcessingException {
exc.setResponse(Response.ok().contentType(APPLICATION_JSON).body(ow.writeValueAsBytes(createDictionaryOfAPIs())).build());
exc.setResponse(ok().contentType(APPLICATION_JSON).body(ow.writeValueAsBytes(createDictionaryOfAPIs())).build());
return RETURN;
}

private Outcome returnHtmlOverview(Exchange exc) {
exc.setResponse(Response.ok().contentType(HTML_UTF_8).body(renderOverviewTemplate()).build());
exc.setResponse(ok().contentType(HTML_UTF_8).body(renderOverviewTemplate()).build());
return RETURN;
}

Expand All @@ -121,8 +122,9 @@ private Outcome returnNoFound(Exchange exc, String id) {
}

private Outcome returnOpenApiAsYaml(Exchange exc, OpenAPIRecord rec) throws IOException, URISyntaxException {
rec.rewriteOpenAPI(exc, getRouter().getUriFactory());
exc.setResponse(Response.ok().contentType(APPLICATION_X_YAML).body(omYaml.writeValueAsBytes(rec.node)).build());
exc.setResponse(ok().yaml()
.body(omYaml.writeValueAsBytes(rec.rewriteOpenAPI(exc, getRouter().getUriFactory())))
.build());
return RETURN;
}

Expand All @@ -147,7 +149,7 @@ private Outcome handleSwaggerUi(Exchange exc) {
return returnNoFound(exc, id);
}

exc.setResponse(Response.ok().contentType(HTML_UTF_8).body(renderSwaggerUITemplate(id, record.api)).build());
exc.setResponse(ok().contentType(HTML_UTF_8).body(renderSwaggerUITemplate(id, record.api)).build());

return RETURN;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public class OpenAPIRecord {
* Specification parsed with JSON/YAML parser
*/
JsonNode node;

OpenAPISpec spec;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ public class Rewrite {
String host;

public JsonNode rewrite(OpenAPIRecord rec, Exchange exc, URIFactory uriFactory) throws URISyntaxException, IOException {
if (rec.isVersion3())
if (rec.isVersion3()) {
return rewriteOpenAPI3(exc, uriFactory, rec.node);
}

return rewriteSwagger2(exc, rec.node);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.predic8.membrane.core.interceptor.ratelimit;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.stream.IntStream;

import static org.junit.jupiter.api.Assertions.*;

class LazyRateLimitTest {

int requestLimit = 100;
LazyRateLimit limiter;

@BeforeEach
void setup() {
limiter = new LazyRateLimit(Duration.ofSeconds(10), requestLimit);
}

@Test
void isRequestLimitReached() {
IntStream.range(0, limiter.getRequestLimit())
.parallel()
.forEach(i -> assertFalse(limiter.isRequestLimitReached("foo")));

assertTrue(limiter.isRequestLimitReached("foo"));
}
}
18 changes: 9 additions & 9 deletions distribution/examples/openapi/openapi-proxy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ service.proxy.bat
curl localhost:2000/api-docs

{
"fruit-shop-api-v1-0" : {
"openapi" : "3.0.2",
"fruitshop-v1-0" : {
"openapi": "3.0.2",
"title" : "Fruit Shop API",
"version" : "1.0",
"openapi_link" : "/api-doc/fruit-shop-api-v1-0",
"ui_link" : "/api-doc/ui/fruit-shop-api-v1-0"
"version": "1.0",
"openapi_link": "/api-docs/fruitshop-v1-0",
"ui_link": "/api-docs/ui/fruitshop-v1-0"
}
}
```
Expand Down Expand Up @@ -70,12 +70,12 @@ The validator checks the request against the OpenAPI definition. Cause the value
```JSON
{
"method": "POST",
"uriTemplate": "/products/",
"path": "/shop/products/",
"uriTemplate": "/products",
"path": "/shop/v2/products",
"validationErrors": {
"REQUEST/BODY#/price": [
{
"message": "-2.7 is smaller than the minimum of 0",
"message": "-2.79 is smaller than the minimum of 0",
"complexType": "Product",
"schemaType": "number"
}
Expand All @@ -84,7 +84,7 @@ The validator checks the request against the OpenAPI definition. Cause the value
}
```

7. Try also a value for _name_ that is longer than 10 characters.
7. Try also a value for _name_ that is longer than 30 characters.

8. Have a look at the configuration in the `proxies.xml`file:

Expand Down
Loading

0 comments on commit 6a6bd6f

Please sign in to comment.