Skip to content

new: unify usage of max request size across all components #293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 18, 2023
Merged
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
2 changes: 1 addition & 1 deletion deploy/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ RUN --mount=type=cache,target=/root/.m2 mvn -f pom.xml clean package -DskipTests
RUN mkdir -p /lowcoder/api-service/plugins /lowcoder/api-service/config /lowcoder/api-service/logs

# Define lowcoder main jar and plugin jars
ARG JAR_FILE=/lowcoder-server/lowcoder-server/target/lowcoder-server-1.0-SNAPSHOT.jar
ARG JAR_FILE=/lowcoder-server/lowcoder-server/target/lowcoder-server-*.jar
ARG PLUGIN_JARS=/lowcoder-server/lowcoder-plugins/*/target/*.jar

# Copy Java runtime for running server
Expand Down
2 changes: 2 additions & 0 deletions deploy/docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Image can be configured by setting environment variables.
| `ENCRYPTION_PASSWORD` | Encryption password | `lowcoder.org` |
| `ENCRYPTION_SALT` | Salt used for encrypting password | `lowcoder.org` |
| `CORS_ALLOWED_DOMAINS` | CORS allowed domains | `*` |
| `LOWCODER_MAX_REQUEST_SIZE` | Lowcoder max request size | `20m` |
| `LOWCODER_API_SERVICE_URL` | Lowcoder API service URL | `http://localhost:8080` |
| `LOWCODER_NODE_SERVICE_URL` | Lowcoder Node service (js executor) URL | `http://localhost:6060` |
| `DEFAULT_ORGS_PER_USER` | Default maximum organizations per user | `100` |
Expand Down Expand Up @@ -121,6 +122,7 @@ Image can be configured by setting environment variables.
| --------------------------------| --------------------------------------------------------------------| ------------------------------------------------------- |
| `PUID` | ID of user running services. It will own all created logs and data. | `9001` |
| `PGID` | ID of group of the user running services. | `9001` |
| `LOWCODER_MAX_REQUEST_SIZE` | Lowcoder max request size | `20m` |
| `LOWCODER_API_SERVICE_URL` | Lowcoder API service URL | `http://localhost:8080` |
| `LOWCODER_NODE_SERVICE_URL` | Lowcoder Node service (js executor) URL | `http://localhost:6060` |

Expand Down
1 change: 1 addition & 0 deletions deploy/docker/docker-compose-multi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ services:
environment:
PUID: "9001"
PGID: "9001"
LOWCODER_MAX_REQUEST_SIZE: 20m
LOWCODER_API_SERVICE_URL: "http://lowcoder-api-service:8080"
LOWCODER_NODE_SERVICE_URL: "http://lowcoder-node-service:6060"
restart: unless-stopped
Expand Down
2 changes: 2 additions & 0 deletions deploy/docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ services:
# api and node service parameters
LOWCODER_API_SERVICE_URL: "http://localhost:8080"
LOWCODER_NODE_SERVICE_URL: "http://localhost:6060"
# frontend parameters
LOWCODER_MAX_REQUEST_SIZE: 20m
volumes:
- ./lowcoder-stacks:/lowcoder-stacks
restart: unless-stopped
Expand Down
6 changes: 4 additions & 2 deletions deploy/docker/frontend/01-update-nginx-conf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ else
ln -s /etc/nginx/nginx-http.conf /etc/nginx/nginx.conf
fi;

sed -i "s@__LOWCODER_MAX_REQUEST_SIZE__@${LOWCODER_MAX_REQUEST_SIZE:=20m}@" /etc/nginx/nginx.conf
sed -i "s@__LOWCODER_API_SERVICE_URL__@${LOWCODER_API_SERVICE_URL:=http://localhost:8080}@" /etc/nginx/nginx.conf
sed -i "s@__LOWCODER_NODE_SERVICE_URL__@${LOWCODER_NODE_SERVICE_URL:=http://localhost:6060}@" /etc/nginx/nginx.conf

echo "nginx config updated with:"
echo " Lowcoder api service URL: ${LOWCODER_API_SERVICE_URL}"
echo " Lowcoder node service URL: ${LOWCODER_NODE_SERVICE_URL}"
echo " Lowcoder max upload size: ${LOWCODER_MAX_REQUEST_SIZE:=20m}"
echo " Lowcoder api service URL: ${LOWCODER_API_SERVICE_URL:=http://localhost:8080}"
echo " Lowcoder node service URL: ${LOWCODER_NODE_SERVICE_URL:=http://localhost:6060}"
1 change: 1 addition & 0 deletions deploy/docker/frontend/nginx-http.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ http {

include /etc/nginx/mime.types;
default_type application/octet-stream;
client_max_body_size __LOWCODER_MAX_REQUEST_SIZE__;

log_format main '"$time_local" client=$remote_addr '
'method=$request_method request="$request" '
Expand Down
1 change: 1 addition & 0 deletions deploy/docker/frontend/nginx-https.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ http {

include /etc/nginx/mime.types;
default_type application/octet-stream;
client_max_body_size __LOWCODER_MAX_REQUEST_SIZE__;

log_format main '"$time_local" client=$remote_addr '
'method=$request_method request="$request" '
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
Expand Down Expand Up @@ -33,8 +35,9 @@ public class CommonConfig {
private String version;
private boolean blockHoundEnable;
private String cookieName;
private int maxQueryRequestSizeInMb = 10;
private int maxQueryResponseSizeInMb = 10;
private String maxUploadSize = "20MB";
private String maxQueryRequestSize = "20MB";
private String maxQueryResponseSize = "20MB";
private Query query = new Query();
private Cookie cookie = new Cookie();
private JsExecutor jsExecutor = new JsExecutor();
Expand All @@ -48,6 +51,37 @@ public boolean isEnterpriseMode() {
return workspace.getMode() == WorkspaceMode.ENTERPRISE;
}

private static final Pattern HUMAN_DATA_SIZE_PATTERN = Pattern.compile("^([ \t]*)(?<number>\\d+(\\.\\d+)?)([ \t]*)(?<unit>[kKmMgGtT]{1})?([bB \t]*)$");
public static String normalizeDataUnits(String dataWithUnits)
{
String normalized = dataWithUnits;
Matcher m = HUMAN_DATA_SIZE_PATTERN.matcher(dataWithUnits);
if (m.matches())
{
normalized = m.group("number");
if (StringUtils.isNotBlank(m.group("unit")))
{
normalized += m.group("unit").toUpperCase() + "B";
}
}
return normalized;
}

public void setMaxUploadSize(String size)
{
this.maxUploadSize = normalizeDataUnits(size);
}

public void setMaxQueryRequestSize(String size)
{
this.maxQueryRequestSize = normalizeDataUnits(size);
}

public void setMaxQueryResponseSize(String size)
{
this.maxQueryResponseSize = normalizeDataUnits(size);
}

@Data
public static class Domain {
private String defaultValue;
Expand Down
4 changes: 2 additions & 2 deletions server/api-service/lowcoder-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@
<artifactId>json-path</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.lowcoder.api.framework.configuration;

import org.lowcoder.sdk.config.CommonConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize;

import jakarta.servlet.MultipartConfigElement;

@Configuration
public class ApplicationConfiguration
{
@Autowired
private CommonConfig common;

@Bean
public MultipartConfigElement multipartConfigElement()
{
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxRequestSize(DataSize.parse(common.getMaxUploadSize()));
factory.setMaxFileSize(DataSize.parse(common.getMaxUploadSize()));
return factory.createMultipartConfig();
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package org.lowcoder.api.framework.filter;

import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import static org.lowcoder.api.framework.filter.FilterOrder.QUERY_EXECUTE_HTTP_BODY_SIZE;
import static org.lowcoder.sdk.exception.BizError.EXCEED_QUERY_REQUEST_SIZE;
import static org.lowcoder.sdk.exception.BizError.EXCEED_QUERY_RESPONSE_SIZE;

import java.util.concurrent.atomic.AtomicLong;

import javax.annotation.Nonnull;

import org.lowcoder.infra.constant.NewUrl;
import org.lowcoder.infra.constant.Url;
import org.lowcoder.sdk.config.CommonConfig;
Expand All @@ -19,19 +24,16 @@
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.unit.DataSize;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;

import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
import java.util.concurrent.atomic.AtomicLong;

import static org.lowcoder.api.framework.filter.FilterOrder.QUERY_EXECUTE_HTTP_BODY_SIZE;
import static org.lowcoder.sdk.exception.BizError.EXCEED_QUERY_REQUEST_SIZE;
import static org.lowcoder.sdk.exception.BizError.EXCEED_QUERY_RESPONSE_SIZE;

/**
* check query request and response size
*/
Expand Down Expand Up @@ -60,11 +62,13 @@ public Mono<Void> filter(@Nonnull ServerWebExchange exchange, @Nonnull WebFilter
// check query api
if (path.startsWith(NewUrl.QUERY_URL) || path.startsWith(Url.QUERY_URL)) {

long maxRequestSize = configInstance.ofLong("maxRequestSize",
commonConfig.getMaxQueryRequestSizeInMb() * FileUtils.ONE_MB);
long maxResponseSize = configInstance.ofLong("maxResponseSize",
commonConfig.getMaxQueryResponseSizeInMb() * FileUtils.ONE_MB);
String maxSize = configInstance.ofString("maxRequestSize", commonConfig.getMaxQueryRequestSize());
long maxRequestSize = DataSize.parse(maxSize).toBytes();
maxSize = configInstance.ofString("maxResponseSize", commonConfig.getMaxQueryResponseSize());
long maxResponseSize = DataSize.parse(maxSize).toBytes();

log.info("Setting up maximum query request size to: {} bytes", maxRequestSize);
log.info("Setting up maximum query response size to: {} bytes", maxResponseSize);
ServerWebExchange newServerWebExchange = exchange.mutate()
.request(new CustomServerHttpRequestDecorator(exchange.getRequest(), maxRequestSize))
.response(new CustomServerHttpResponseDecorator(exchange.getResponse(), maxResponseSize))
Expand Down Expand Up @@ -126,4 +130,5 @@ public Mono<Void> writeWith(@Nonnull Publisher<? extends DataBuffer> body) {
}));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ common:
block-hound-enable: false
js-executor:
host: ${LOWCODER_NODE_SERVICE_URL:http://127.0.0.1:6060}
max-query-request-size-in-mb: 20
max-query-response-size-in-mb: 20
max-query-request-size: ${LOWCODER_MAX_REQUEST_SIZE:20m}
max-query-response-size: ${LOWCODER_MAX_REQUEST_SIZE:20m}
max-upload-size: ${LOWCODER_MAX_REQUEST_SIZE:20m}

material:
mongodb-grid-fs:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package org.lowcoder.api.framework.configuration;

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

import org.junit.jupiter.api.Test;
import org.lowcoder.sdk.config.CommonConfig;

public class CommonConfigTest
{

/**
* Test parsing with nginx notations (k, m, g)
**/

@Test
public void normalizeDataUnit_worksWithNginxBytes()
{
String nginx = "123456";
assertEquals("123456", CommonConfig.normalizeDataUnits(nginx));
}

@Test
public void normalizeDataUnit_worksWithNginxKiloBytes()
{
String nginx = "16k";
assertEquals("16KB", CommonConfig.normalizeDataUnits(nginx));
}

@Test
public void normalizeDataUnit_worksWithNginxMegaBytes()
{
String nginx = "20m";
assertEquals("20MB", CommonConfig.normalizeDataUnits(nginx));
}

@Test
public void normalizeDataUnit_worksWithNginxGigaBytes()
{
String nginx = "20g";
assertEquals("20GB", CommonConfig.normalizeDataUnits(nginx));
}

/**
* Test parsing with spring notations (B, KB, MB, GB)
**/

@Test
public void normalizeDataUnit_worksWithSpringBytes()
{
String nginx = "123456B";
assertEquals("123456", CommonConfig.normalizeDataUnits(nginx));
}

@Test
public void normalizeDataUnit_worksWithSpringKiloBytes()
{
String nginx = "16KB";
assertEquals("16KB", CommonConfig.normalizeDataUnits(nginx));
}

@Test
public void normalizeDataUnit_worksWithSpringMegaBytes()
{
String nginx = "20MB";
assertEquals("20MB", CommonConfig.normalizeDataUnits(nginx));
}

@Test
public void normalizeDataUnit_worksWithSpringGigaBytes()
{
String nginx = "20GB";
assertEquals("20GB", CommonConfig.normalizeDataUnits(nginx));
}

/**
* Test parsing with mixed case
**/

@Test
public void normalizeDataUnit_worksWithMixedCase()
{
assertEquals("10MB", CommonConfig.normalizeDataUnits("10MB"));
assertEquals("10MB", CommonConfig.normalizeDataUnits("10mb"));
assertEquals("10MB", CommonConfig.normalizeDataUnits("10mB"));
assertEquals("10MB", CommonConfig.normalizeDataUnits("10m"));
assertEquals("10MB", CommonConfig.normalizeDataUnits("10M"));
}

/**
* Test parsing with whitespaces
**/

@Test
public void normalizeDataUnit_worksWithWhitespaces()
{
assertEquals("10MB", CommonConfig.normalizeDataUnits("10 MB"));
assertEquals("10MB", CommonConfig.normalizeDataUnits("10M "));
assertEquals("10MB", CommonConfig.normalizeDataUnits("10\tmb"));
assertEquals("10MB", CommonConfig.normalizeDataUnits(" 10 MB "));
assertEquals("10MB", CommonConfig.normalizeDataUnits("\t10\tMB\t"));
assertEquals("10MB", CommonConfig.normalizeDataUnits(" \t 10 \tMB \t"));
}
}
10 changes: 5 additions & 5 deletions server/api-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,11 @@
<version>1.0.6.RELEASE</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
</dependency>

<dependency>
<groupId>io.projectreactor</groupId>
Expand Down