Skip to content
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

Ratelimiting #800

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1edfa99
ratelimiting (in progress)
ValliC444 Mar 29, 2020
46e9134
ratelimiting (in progress)
ValliC444 Mar 29, 2020
7cbd4bd
ratelimiting (in progress)
ValliC444 Mar 29, 2020
6fb0196
ratelimiting (in progress)
ValliC444 Mar 29, 2020
5548861
ratelimiting (in progress)
ValliC444 Mar 29, 2020
bd255a2
ratelimiting (in progress)
ValliC444 Mar 29, 2020
af4c28f
ratelimiting (in progress)
ValliC444 Mar 29, 2020
3b059b0
ratelimiting (in progress)
ValliC444 Mar 29, 2020
6f3d83a
ratelimiting (in progress)
ValliC444 Mar 29, 2020
97f37fb
ratelimiting (in progress)
ValliC444 Mar 29, 2020
f157b7a
ratelimiting (in progress)
ValliC444 Mar 29, 2020
2b94a99
ratelimiting (in progress)
ValliC444 Mar 29, 2020
a97bc71
ratelimiting (in progress)
ValliC444 Mar 29, 2020
a64dfc6
ratelimiting (in progress)
ValliC444 Mar 29, 2020
a309580
ratelimiting (in progress)
ValliC444 Mar 29, 2020
1b3e4c7
ratelimiting (in progress)
ValliC444 Mar 29, 2020
fe3cd7a
ratelimiting (in progress)
ValliC444 Mar 29, 2020
0f980e2
ratelimiting (in progress)
ValliC444 Mar 29, 2020
e791ae7
ratelimiting (in progress)
ValliC444 Mar 30, 2020
f2c4b39
bucket4j implementation for imi (in progress)
ValliC444 Apr 4, 2020
529505f
bucket4j implementation for imi and other modules (in progress)
ValliC444 Apr 12, 2020
2451b78
bucket4j implementation for imi and other modules (in progress)
ValliC444 Apr 13, 2020
7adaa24
api rate limiting (testing)
ValliC444 Apr 13, 2020
0d0cc15
api rate limiting (testing)
ValliC444 Apr 13, 2020
f9edc0a
rate limiting for all kilkari and MA Apis
ValliC444 Apr 13, 2020
f1ef33e
made changes to fix errors in email alert functionality
ValliC444 Apr 15, 2020
17c6cab
minor changes
ValliC444 Apr 20, 2020
efcfbed
Added custom error message for CDR/CSR file copying errors
ValliC444 Apr 20, 2020
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 api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
<version>${motech.version}</version>
</dependency>

<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>org.motechproject.com.github.vladimir-bukhtoyarov</artifactId>
<version>4.1.1-${external.dependency.release.tag.new}</version>
</dependency>

<dependency>
<groupId>org.motechproject.nms</groupId>
<artifactId>region</artifactId>
Expand Down
80 changes: 80 additions & 0 deletions api/src/main/resources/META-INF/motech/applicationContext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,86 @@

<mvc:annotation-driven />

<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/kilkari/user"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="kilkari_user"></constructor-arg>
<constructor-arg type="int" value="100"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/kilkari/inbox"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="kilkari_inbox"></constructor-arg>
<constructor-arg type="int" value="100"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/kilkari/subscription"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="kilkari_subscription"></constructor-arg>
<constructor-arg type="int" value="200"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/kilkari/inboxCallDetails"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="kilkari_inboxCallDetails"></constructor-arg>
<constructor-arg type="int" value="100"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/mobileacademy/user"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="mobileacademy_user"></constructor-arg>
<constructor-arg type="int" value="100"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/mobileacademy/sms/status/imi"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="sms_status_imi"></constructor-arg>
<constructor-arg type="int" value="100"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/mobileacademy/course"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="mobileacademy_course"></constructor-arg>
<constructor-arg type="int" value="100"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/mobileacademy/courseVersion"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="mobileacademy_courseVersion"></constructor-arg>
<constructor-arg type="int" value="100"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/mobileacademy/bookmarkWithScore"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="mobileacademy_bookmarkWithScore"></constructor-arg>
<constructor-arg type="int" value="200"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/mobileacademy/callDetails"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="mobileacademy_callDetails"></constructor-arg>
<constructor-arg type="int" value="100"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/mobileacademy/languageLocationCode"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="mobileacademy_languageLocationCode"></constructor-arg>
<constructor-arg type="int" value="100"></constructor-arg>
</bean>
</mvc:interceptor>
</mvc:interceptors>

<bean id="moduleRegistrationData" class="org.motechproject.osgi.web.ModuleRegistrationData">
<constructor-arg name="moduleName" value="api" />
<constructor-arg name="i18n">
Expand Down
8 changes: 8 additions & 0 deletions imi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@
<version>${motech.version}</version>
</dependency>

<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>org.motechproject.com.github.vladimir-bukhtoyarov</artifactId>
<version>4.1.1-${external.dependency.release.tag.new}</version>
</dependency>


<!-- common properties -->
<dependency>
Expand Down Expand Up @@ -55,6 +61,7 @@
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

</dependencies>

<repositories>
Expand Down Expand Up @@ -97,6 +104,7 @@
net.sf.cglib.reflect,
org.springframework.transaction,
org.motechproject.nms.kilkari.repository,
org.motechproject.nms.kilkari.service,
org.motechproject.nms.region.service,
org.motechproject.nms.region.utils,
org.datanucleus.enhancement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public void notifyNewCdrFile(@RequestBody CdrFileNotificationRequest request) {
null
));
alertService.create(fileName, "scpCdrFromRemote", error, AlertType.CRITICAL, AlertStatus.NEW, 0, null);
throw new IllegalArgumentException(e.getMessage(), e);
throw new IllegalArgumentException("Error copying CDR file", e);
}

// Copy the summary file from the IMI share (imi.remote_cdr_dir) into local cdr dir (imi.local_cdr_dir)
Expand All @@ -231,7 +231,7 @@ public void notifyNewCdrFile(@RequestBody CdrFileNotificationRequest request) {
null
));
alertService.create(fileName, "scpCdrFromRemote", error, AlertType.CRITICAL, AlertStatus.NEW, 0, null);
throw new IllegalArgumentException(e.getMessage(), e);
throw new IllegalArgumentException("Error copying CSR file", e);
}

// Checks the CSR/CDR checksum, record count & csv, then sends an event to proceed to phase 2 of the
Expand Down
17 changes: 16 additions & 1 deletion imi/src/main/resources/META-INF/motech/applicationContext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,22 @@
<context:component-scan base-package="org.motechproject.nms.imi" />

<mvc:annotation-driven />

<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/obdFileProcessedStatusNotification"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="obdFileProcessedStatusNotification"></constructor-arg>
<constructor-arg type="int" value="10"></constructor-arg>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/cdrFileNotification"/>
<bean class="org.motechproject.nms.kilkari.service.RateLimitInterceptor">
<constructor-arg type="java.lang.String" value="cdrFileNotification"></constructor-arg>
<constructor-arg type="int" value="10"></constructor-arg>
</bean>
</mvc:interceptor>
</mvc:interceptors>
<bean id="moduleRegistrationData" class="org.motechproject.osgi.web.ModuleRegistrationData">
<constructor-arg name="moduleName" value="imi" />
<constructor-arg name="i18n">
Expand Down
4 changes: 4 additions & 0 deletions kilkari/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
<artifactId>rejection-handler</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>org.motechproject.com.github.vladimir-bukhtoyarov</artifactId>
</dependency>

</dependencies>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.motechproject.nms.kilkari.service;

import io.github.bucket4j.*;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

public class RateLimitInterceptor implements HandlerInterceptor {
//
private final Bucket bucket;
private final String url = "http://192.168.200.4:8080/NMSReportingSuite/nms/mail/emailAlert";
private final String api;
private final String USER_AGENT = "Mozilla/5.0";
private final int capacity;
private final String email = "[email protected]";

public RateLimitInterceptor(int capacity, String api) {
this.capacity = capacity;
Refill refill = Refill.intervally(capacity, Duration.ofMinutes(1));
Bandwidth limit = Bandwidth.classic(capacity, refill);
this.bucket = Bucket4j.builder().addLimit(limit).build();
this.api = api;
}

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("Inside pre handle");
ConsumptionProbe probe = this.bucket.tryConsumeAndReturnRemaining(1);

if (probe.isConsumed()) {
long remTokens = probe.getRemainingTokens();
response.addHeader("X-Rate-Limit-Remaining",
Long.toString(remTokens));
System.out.println("remaning tokens for this api for this min "+remTokens);
if(remTokens==(0.4*capacity)){
sendEmailAlert(remTokens, "warning", 60);
System.out.println("sendEmailAlert "+api + remTokens);
}
if(remTokens==(0.2*capacity)){
sendEmailAlert(remTokens, "critical", 80);
}
return true;
}

response.setStatus(429); // 429
response.addHeader("X-Rate-Limit-Retry-After-Milliseconds",
Long.toString(TimeUnit.NANOSECONDS.toMillis(probe.getNanosToWaitForRefill())));

return false;
}

@Override
public void postHandle(HttpServletRequest req, HttpServletResponse res,
Object handler, ModelAndView model) throws Exception {
}

// Called after rendering the view
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res,
Object handler, Exception ex) throws Exception {
}

public void sendEmailAlert(long remTokens, String status, long perc) throws Exception {
String finalUrl = url + "?api=" + api + "&capacity=" + remTokens + "&email=" + email + "&status=" + status
+ "&perc=" + perc;
URL obj = new URL(finalUrl);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();

// optional default is GET
con.setRequestMethod("GET");

//add request header
con.setRequestProperty("User-Agent", USER_AGENT);

int responseCode = con.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + finalUrl);
System.out.println("Response Code : " + responseCode);
}

}
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>org.motechproject.com.github.vladimir-bukhtoyarov</artifactId>
<version>4.1.1-${external.dependency.release.tag.new}</version>
</dependency>
<dependency>
<groupId>org.motechproject</groupId>
<artifactId>motech-platform-osgi-extender-fragment</artifactId>
Expand Down