From 8dca792ef8df56b818f9f691ebf41bf6dfd7d16c Mon Sep 17 00:00:00 2001 From: Ruben Date: Wed, 5 Oct 2016 21:42:27 +0200 Subject: [PATCH] Throttling middleware test --- bin/generate_db.php | 287 ++------------------------------- composer.json | 9 +- src/Api/ThrottleMiddleware.php | 77 +++++++++ www/index.php | 5 +- 4 files changed, 102 insertions(+), 276 deletions(-) create mode 100644 src/Api/ThrottleMiddleware.php diff --git a/bin/generate_db.php b/bin/generate_db.php index 44a663d..ed9e293 100644 --- a/bin/generate_db.php +++ b/bin/generate_db.php @@ -37,6 +37,16 @@ foreach($queries as $sql){ echo "$sql;\n"; } // updateSchema $tool->updateSchema($classes); +$em->getConnection()->query(" +CREATE TABLE IF NOT EXISTS `TokenBucket` ( + `name` varchar(128) NOT NULL, + `value` varchar(255) NOT NULL +) ENGINE=MEMORY DEFAULT CHARSET=utf8; + +ALTER TABLE `TokenBucket` + ADD PRIMARY KEY (`name`); + "); + // ADD EMOTIONS @@ -99,7 +109,7 @@ $i++; $lvl->createTarget($emotions['fear'], 100, $i++); $lvl->createTarget($emotions['fear'], 100, $i++); $lvl->createTarget($emotions['fear'], 100, $i++); -$em->persist($lvl);*/ +$em->persist($lvl); $lvl = new EmotionHero\Models\Level(); $lvl->setId(6); @@ -333,228 +343,13 @@ $lvl->createTarget($emotions['surprise'], 1, $a); $em->persist($lvl); - +*/ $em->flush(); die(); - // ////////////////// new levels: - - // case LVL_SMILE: - // int s = 2; - // setTarget(Emotion.JOY, 100, s++); - // setTarget(Emotion.JOY, 100, s++); - // setTarget(Emotion.JOY, 50, s++); - // setTarget(Emotion.JOY, 50, s++); - // setTarget(Emotion.JOY, 30, s++); - // setTarget(Emotion.JOY, 30, s++); - // setTarget(Emotion.JOY, 50, s++); - // setTarget(Emotion.JOY, 50, s++); - // setTarget(Emotion.JOY, 100, s++); - // setTarget(Emotion.JOY, 100, s++); - - // $s++; - // $s++; - - // setTarget(Emotion.SURPRISE, 100, s++); - // setTarget(Emotion.SURPRISE, 100, s++); - // setTarget(Emotion.SURPRISE, 50, s++); - // setTarget(Emotion.SURPRISE, 50, s++); - // setTarget(Emotion.SURPRISE, 100, s++); - // setTarget(Emotion.SURPRISE, 100, s++); - - // $s++; - // $s++; - // $s++; - // setTarget(Emotion.JOY, 100, s++); - // setTarget(Emotion.SURPRISE, 100, s); - // setTarget(Emotion.JOY, 100, s++); - // setTarget(Emotion.SURPRISE, 100, s); - // setTarget(Emotion.JOY, 100, s++); - // setTarget(Emotion.SURPRISE, 50, s); - // setTarget(Emotion.SURPRISE, 30, s); - - // $s++; - // $s++; - // setTarget(Emotion.JOY, 100, s++); - // setTarget(Emotion.ANGER, 1, s); - // setTarget(Emotion.CONTEMPT, 1, s); - // setTarget(Emotion.DISGUST, 1, s); - - // $s++; - // $s++; - // setTarget(Emotion.DISGUST, 1, s++); - // setTarget(Emotion.ANGER, 1, s); - // setTarget(Emotion.SURPRISE, 100, s); - - // setMinimumScoreFromPercentage(40); - // minimumAchievements = 2; - // achievements.add(achievementCollection.get(5)); - // achievements.add(achievementCollection.get(6)); - // achievements.add(achievementCollection.get(7)); - // break; - // case LVL_BUSINESS: - // int b = 2; - - // setTarget(Emotion.ANGER, 100, b++); - // setTarget(Emotion.ANGER, 100, b++); - // setTarget(Emotion.ANGER, 50, b++); - // setTarget(Emotion.ANGER, 50, b++); - // setTarget(Emotion.ANGER, 30, b++); - // setTarget(Emotion.ANGER, 30, b++); - // $b++; - // setTarget(Emotion.DISGUST, 100, b++); - // setTarget(Emotion.DISGUST, 50, b++); - // setTarget(Emotion.DISGUST, 50, b++); - // setTarget(Emotion.DISGUST, 30, b++); - // $b++; - // setTarget(Emotion.CONTEMPT, 100, b++); - // setTarget(Emotion.CONTEMPT, 50, b++); - // setTarget(Emotion.CONTEMPT, 50, b++); - // setTarget(Emotion.CONTEMPT, 30, b++); - // $b++; - // setTarget(Emotion.SURPRISE, 100, b++); - - // setTarget(Emotion.SURPRISE, 100, b++); - // setTarget(Emotion.SADNESS, 50, b); - - // setTarget(Emotion.JOY, 50, b++); - // setTarget(Emotion.DISGUST, 50, b++); - // setTarget(Emotion.CONTEMPT, 30, b++); - // setTarget(Emotion.CONTEMPT, 100, b++); - // setTarget(Emotion.JOY, 100, b++); - // setTarget(Emotion.SURPRISE, 100, b++); - - // b++; - // setTarget(Emotion.SADNESS, 100, b++); - // setTarget(Emotion.ANGER, 10, b++); - // setTarget(Emotion.SADNESS, 100, b++); - // setTarget(Emotion.ANGER, 10, b++); - // setTarget(Emotion.SADNESS, 100, b++); - // setTarget(Emotion.ANGER, 10, b++); - - - // setMinimumScoreFromPercentage(40); - // minimumAchievements = 2; - // achievements.add(achievementCollection.get(8)); - // achievements.add(achievementCollection.get(9)); - // achievements.add(achievementCollection.get(10)); - // break; - // case LVL_REALLY: - // int r = 2; - // setTarget(Emotion.FEAR, 100, r++); - // setTarget(Emotion.FEAR, 50, r++); - // setTarget(Emotion.FEAR, 30, r++); - - // r++; - // setTarget(Emotion.SADNESS, 100, r++); - // setTarget(Emotion.SADNESS, 50, r++); - // setTarget(Emotion.SADNESS, 100, r++); - - // r++; - // setTarget(Emotion.DISGUST, 100, r++); - // setTarget(Emotion.DISGUST, 100, r++); - // setTarget(Emotion.DISGUST, 50, r++); - - // r++; - // setTarget(Emotion.DISGUST, 100, r++); - // setTarget(Emotion.CONTEMPT, 30, r); - // setTarget(Emotion.DISGUST, 50, r++); - // setTarget(Emotion.CONTEMPT, 50, r); - // setTarget(Emotion.DISGUST, 30, r++); - // setTarget(Emotion.CONTEMPT, 100, r); - - // r++; - // r++; - // setTarget(Emotion.SADNESS, 100, r++); - // setTarget(Emotion.JOY, 50, r++); - // setTarget(Emotion.SADNESS, 100, r++); - // setTarget(Emotion.JOY, 50, r++); - // setTarget(Emotion.SADNESS, 50, r++); - // setTarget(Emotion.JOY, 100, r++); - // setTarget(Emotion.SADNESS, 50, r++); - // setTarget(Emotion.JOY, 100, r); - // setTarget(Emotion.SADNESS, 100, r++); - // setTarget(Emotion.JOY, 50, r); - - - // setMinimumScoreFromPercentage(40); - // minimumAchievements = 2; - // achievements.add(achievementCollection.get(8)); - // achievements.add(achievementCollection.get(9)); - // achievements.add(achievementCollection.get(10)); - // achievements.add(achievementCollection.get(11)); - - // break; - // case LVL_ACTNORMAL: - // float a = 2; - // setTarget(Emotion.ANGER, 100, a++); - // setTarget(Emotion.DISGUST, 100, a++); - // setTarget(Emotion.FEAR, 100, a++); - // setTarget(Emotion.JOY, 100, a++); - // setTarget(Emotion.SADNESS, 100, a++); - // setTarget(Emotion.SURPRISE, 100, a++); - - // $a++; - // setTarget(Emotion.ANGER, 30, a++); - // setTarget(Emotion.DISGUST, 30, a+=0.5); - // setTarget(Emotion.FEAR, 30, a+=0.5); - // setTarget(Emotion.JOY, 30, a+=0.5); - // setTarget(Emotion.SADNESS, 30, a+=0.5); - // setTarget(Emotion.SURPRISE, 30, a+=0.5); - - // $a++; - // setTarget(Emotion.ANGER, 30, a++); - // setTarget(Emotion.DISGUST, 30, a); - // setTarget(Emotion.ANGER, 30, a++); - // setTarget(Emotion.DISGUST, 30, a); - - // setTarget(Emotion.FEAR, 30, a++); - // setTarget(Emotion.JOY, 30, a); - // setTarget(Emotion.FEAR, 30, a++); - // setTarget(Emotion.JOY, 30, a); - - // setTarget(Emotion.SADNESS, 30, a++); - // setTarget(Emotion.SURPRISE, 30, a); - // setTarget(Emotion.SADNESS, 30, a++); - // setTarget(Emotion.SURPRISE, 30, a); - - // $a++; - // $a++; - // setTarget(Emotion.DISGUST, 20, a++); - // setTarget(Emotion.CONTEMPT, 20, a); - // setTarget(Emotion.SURPRISE, 20, a); - - // setTarget(Emotion.DISGUST, 10, a++); - // setTarget(Emotion.CONTEMPT, 10, a); - // setTarget(Emotion.SURPRISE, 10, a); - - // setTarget(Emotion.DISGUST, 5, a++); - // setTarget(Emotion.CONTEMPT, 5, a); - // setTarget(Emotion.SURPRISE, 5, a); - - // // Kuleshov's target: - // kuleshovTargetIds = new int[3]; - // kuleshovTargetIds[0] = setTarget(Emotion.DISGUST, 1, a++); - // kuleshovTargetIds[1] = setTarget(Emotion.CONTEMPT, 1, a); - // kuleshovTargetIds[2] = setTarget(Emotion.SURPRISE, 1, a); - // // END OF KULESHOV - - // setTarget(Emotion.DISGUST, 1, a++); - // setTarget(Emotion.CONTEMPT, 1, a); - // setTarget(Emotion.SURPRISE, 1, a); - - - // setTarget(Emotion.ANGER, 1, a++); - // setTarget(Emotion.CONTEMPT, 1, a); - // setTarget(Emotion.DISGUST, 1, a); - // setTarget(Emotion.FEAR, 1, a); - // setTarget(Emotion.JOY, 1, a); - // setTarget(Emotion.SADNESS, 1, a); - // setTarget(Emotion.SURPRISE, 1, a); -die(); - + // TEST USER - +die(); if(empty($em->getRepository(EmotionHero\Models\User::class)->findOneBy([]))) { $user = new EmotionHero\Models\User(); $em->persist($user); @@ -562,57 +357,3 @@ if(empty($em->getRepository(EmotionHero\Models\User::class)->findOneBy([]))) { $em->flush(); } -// $lvl = new EmotionHero\Models\Level(); -// $lvl->setId(1); -// $lvl->setName("I am sooo ANGRY"); -// $lvl->createTarget($emotions['anger'], 100, 1); -// $lvl->createTarget($emotions['anger'], 100, 2); -// $lvl->createTarget($emotions['anger'], 10, 3); -// $lvl->createTarget($emotions['anger'], 20, 4); -// $lvl->createTarget($emotions['anger'], 40, 5); -// $lvl->createTarget($emotions['anger'], 70, 6); -// $lvl->createTarget($emotions['anger'], 100, 7); -// $em->persist($lvl); - - -// $lvl = new EmotionHero\Models\Level(); -// $lvl->setId(2); -// $lvl->setName("Let's be joyfull!"); -// $lvl->createTarget($emotions['joy'], 100, 1); -// $lvl->createTarget($emotions['joy'], 100, 2); -// $lvl->createTarget($emotions['joy'], 100, 4); -// $lvl->createTarget($emotions['contempt'], 20, 4); -// $lvl->createTarget($emotions['anger'], 100, 5); -// $lvl->createTarget($emotions['joy'], 100, 7); -// $lvl->createTarget($emotions['anger'], 100, 9); -// $lvl->createTarget($emotions['joy'], 100, 11); -// $lvl->createTarget($emotions['joy'], 70, 12); -// $lvl->createTarget($emotions['joy'], 60, 13); -// $lvl->createTarget($emotions['joy'], 30, 14); -// $lvl->createTarget($emotions['joy'], 10, 14.5); -// $lvl->createTarget($emotions['anger'], 100, 16); -// $lvl->createTarget($emotions['joy'], 100, 17); -// $lvl->createTarget($emotions['joy'], 100, 18); -// $lvl->createTarget($emotions['joy'], 100, 19); -// $lvl->createTarget($emotions['joy'], 100, 20); -// $em->persist($lvl); - -// $lvl = new EmotionHero\Models\Level(); -// $lvl->setId(3); -// $lvl->setName("What a surprise"); -// $lvl->createTarget($emotions['surprise'], 20, 1); -// $lvl->createTarget($emotions['surprise'], 50, 2); -// $lvl->createTarget($emotions['surprise'], 80, 3); -// $lvl->createTarget($emotions['surprise'], 100, 4); -// $em->persist($lvl); - -// $lvl = new EmotionHero\Models\Level(); -// $lvl->setId(4); -// $lvl->setName("Please, don't cry..."); -// $lvl->createTarget($emotions['sadness'], 20, 1); -// $lvl->createTarget($emotions['sadness'], 50, 2); -// $lvl->createTarget($emotions['sadness'], 80, 3); -// $lvl->createTarget($emotions['sadness'], 100, 4); -// $em->persist($lvl); - -$em->flush(); diff --git a/composer.json b/composer.json index fcb5b96..c2f70cd 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,11 @@ { "name": "emotionhero/emotionhero_api", + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/rubenvandeven/tokenbucket" + } + ], "description": "The API for the Emotion Hero game.", "require": { "silex/silex": "~2.0", @@ -9,7 +15,8 @@ "symfony/serializer": "^3.1", "cnam/security-jwt-service-provider": "2.*", "jms/serializer": "^1.3", - "danielstjules/php-pretty-datetime": "dev-master" + "danielstjules/php-pretty-datetime": "dev-master", + "fustundag/tokenbucket": "dev-master" }, "autoload": { "psr-4": { diff --git a/src/Api/ThrottleMiddleware.php b/src/Api/ThrottleMiddleware.php new file mode 100644 index 0000000..505e109 --- /dev/null +++ b/src/Api/ThrottleMiddleware.php @@ -0,0 +1,77 @@ +app = $app; + $this->options = array_merge([ + 'pdo'=>null, + ], $options); + if($this->options['pdo'] === null) { + throw new Exception("Please profide the PDO handle with the 'pdo' option", 1); + } + } + + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + $limit_requests = true; + + // don't throttle on subrequest (internal) + if($type != HttpKernelInterface::MASTER_REQUEST) { + $limit_requests = false; + } else { + // limited throttling for non-users (at ip level) + $tokens = 15; // max nr of tokens (also start amount) + $rate = 0.5; // tokens per seconds + $name = 'ip_' . $request->getClientIp(); + } + + if($limit_requests) + { + $storage = new PDOStorage($this->options['pdo']); + // Define the bucket + $bucket_options = array( + 'capacity' => $tokens, + 'fillRate' => $rate + ); + // Create the bucket + $bucket = new TokenBucket($name, $storage, $bucket_options); + + if ($bucket->consume()===false) { + return new JsonResponse(['success' => false, 'message' => "Too many requests"], 429); + } + } + + // don't return null (which would yield almost the same result), so we can + // add the headers to the response afterwards. + $response = $this->app->handle($request, $type, $catch); + + if(isSet($bucket)) + { + foreach($bucket->getRateLimitHttpHeaders() as $header_name => $header_value) + { + $response->headers->set($header_name, $header_value); + } + } + + return $response; + + } +} diff --git a/www/index.php b/www/index.php index 0209527..781d592 100644 --- a/www/index.php +++ b/www/index.php @@ -166,6 +166,7 @@ $app->get('/api/protected_resource', function() use ($app){ $app->mount('/', new EmotionHero\Api\ScoreControllerProvider()); +// middlewares +$appStack = new EmotionHero\Api\ThrottleMiddleware($app, ['pdo'=>$eh->getEm()->getConnection()] ); - -$app->run(); \ No newline at end of file +$appStack->handle(Request::createFromGlobals())->send(); \ No newline at end of file