برای شروع، از شما میخواهیم به کسانی که بعد از شما وارد مسابقه میشوند خوشآمد بگویید. برای این منظور یک تابع با نام `HelloCodeCup` تعریف کردهایم.
اما از آنجا که میخواهیم برنامه برای مسابقات بعدی کدکاپ نیز قابل استفاده باشد، از کاربر میخواهیم که شمارهی مسابقه را به تابع بدهد، سپس براساس آن، عبارت خوشآمدگویی مناسب را برگردانیم.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/35060/download_problem_initial_project/126059/) دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
```
hello-codecup
├── go.mod
├── go.sum
├── main.go
└── main_sample_test.go
```
در فایل `main.go` تابعی با نام `HelloCodeCup` تعریف شده که امضای آن بهصورت زیر است:
```go
func HelloCodeCup(n int) string {
// TODO: Implement
}
```
این تابع را طوری پیادهسازی کنید که به ازای ورودی `n`، رشتهی `Hello Codecup n` را برگرداند.
# مثال
به ازای ورودی `6`، تابع باید چنین مقداری را برگرداند:
```
Hello CodeCup 6
```
**توجه:** در این سؤال مجاز به استفاده از کتابخانههای شخص ثالث نیستید.
# آنچه باید آپلود کنید
پس از پیادهسازی تابع `HelloCodeCup`، فایل `main.go` را آپلود کنید.
پس از کنکور سخت و طاقتفرسا، مهدی تصمیم گرفته سریال جدیدی را شروع کند.
برای این منظور، چند قسمت از آن سریال را دانلود کرده (ویدیوهایی با فرمت `mkv`) و همچنین از آنجا که زبانش خوب نیست، یک سری زیرنویس (با فرمت `srt`) هم برای برخی قسمتها دانلود کرده است.
او همهی فایلها را در یک پوشه ریخته، اما مشکلی که وجود دارد این است که برای این که زیرنویسها به درستی کنار ویدیوها پخش شوند، باید هر زیرنویس دقیقاً همنام فایل ویدیویی باشد، با این تفاوت که *extension* ها متفاوت است.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/35060/download_problem_initial_project/126060/) دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
```
subtitles
├── go.mod
├── go.sum
├── main.go
├── main_sample_test.go
├── sub.srt
└── video.mkv
```
مهدی میخواهد در فایل `main.go` تابعی با نام `Renamify` پیادهسازی کند که زیرنویسها را همنام با ویدیوها کند. این تابع آدرس یک دایرکتوری را دریافت کرده و باید عملیات تغییر نام را انجام دهد:
```go
func Renamify(path string) {
// TODO: Implement
}
```
مثلاً پوشهی ورودی میتواند به این شکل باشد:
```
Serial1.S01E01.mkv
Serial1.sub.S01E01.srt
```
ولی میخواهیم زیرنویسها به ویدیوی متناظر خود تغییر نام داده شوند؛ مثلاً پوشهی نهایی چنین محتویاتی داشته باشد:
```
Serial1.S01E01.mkv
Serial1.S01E01.srt
```
# مثال
## نمونه پوشهی ورودی
```
serial1.S1E1.mkv
serial1.S1E2.mkv
serial1.S1E3.mkv
subof.series1.S01E01.srt
subof.series1.S01E02.srt
subof.series1.S01E03.srt
```
## نمونه پوشهی خروجی
```
serial1.S1E1.mkv
serial1.S1E2.mkv
serial1.S1E3.mkv
serial1.S1E1.srt
serial1.S1E2.srt
serial1.S1E3.srt
```
# نکات
+ دقیقاً یک سریال در پوشه موجود است.
+ از هر قسمت **حداکثر** یک زیرنویس و **حداکثر** یک ویدیو داریم.
+ به جز فایلهای `mkv` (ویدیوها) و فایلهای `srt` (زیرنویسها) هیچ فایل دیگری در پوشه موجود نیست.
+ نام همهی فایلها، اطلاعات شماره قسمت و فصل مربوطه را به فرم `S01E01` در خود دارد.
+ در این سؤال مجاز به استفاده از کتابخانههای شخص ثالث نیستید.
+ شما تنها مجاز به اعمال تغییرات در فایل `main.go` هستید.
# آنچه باید آپلود کنید
پس از پیادهسازی تابع `Renamify`، فایل `main.go` را آپلود کنید.
یک هکر کلاهسفید قصد دارد سایتهای مختلف برای برای آسیبپذیری [Log4jShell](https://thehackernews.com/2021/12/apache-log4j-vulnerability-log4shell.html) بررسی کند. همانطور که میدانید، این یک آسیبپذیری از نوع *RCE* است و میتواند بسیار مخرب باشد؛ بنابراین باید به سرعت تشخیص داده شود.
این هکر برای بررسی یک سایت، باید تمام مسیرهای ممکن آن را بررسی کند. برای این که از روی آدرس سایت تمام مسیرهای آن را پیدا کند، از [*HATEOAS*](https://en.wikipedia.org/wiki/HATEOAS) استفاده میکند (فرض کنیم همهی سایتها *HATEOAS* دارند).
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/35060/download_problem_initial_project/126062/) دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
```
log4j
├── go.mod
├── go.sum
├── main.go
├── main_sample_test.go
└── utils.go
```
هکر تاکنون یک تابع `Retrieve` در فایل `utils.go` نوشته که با دریافت آدرس یک مسیر، مسیرهای دیگری که از این مسیر امکان رفتن بهصورت **مستقیم** دارند را به او میدهد. مثلاً به ازای ورودی `snapp.ir/`، آدرسهای زیر را برمیگرداند:
+ `snapp.ir/blog`
+ `snapp.ir/contact`
+ `snapp.ir/about`
و به ازای ورودی `snapp.ir/blog`، آدرس های زیر را برمیگرداند:
+ `snapp.ir/blog/post1`
+ `snapp.ir/blog/post2`
نسخهی اصلی این تابع که هکر پیادهسازی کرده، به آدرس موردنظر درخواست *HTTP* ارسال میکند و نتیجه را پردازش کرده و لینک مسیرها را استخراج گرده و برمیگرداند. همانطور که حدس میزنید، هر بار اجرای این تابع با توجه به این که از شبکه استفاده میکند تا به سرور دسترسی پیدا کند، بسیار زمانبر است.
علاوه بر تابع `Retrieve`، یک تابع به نام `Hack` نوشته که قرار است به شکل بازگشتی، از ریشهی یک سایت شروع کرده و تمام مسیرهایی که به شکل مستقیم یا غیرمستقیم میتوان به آنها رسید را پیدا کند (به شکل بدون ترتیب). پیادهسازی اولیهای نیز از این تابع در اختیار ما قرار گرفته، اما متأسفانه با توجه به کندی تابع `Retrieve`، بسیار کند است.
```go
package main
func Hack(start string) []string {
finalResult := []string{}
tempResult := Retrieve(start)
finalResult = append(finalResult, tempResult...)
for _, v := range tempResult {
finalResult = append(finalResult, Hack(v)...)
}
return finalResult
}
```
هکر کلاهسفید از شما خواسته تا بدون تغییر عملکرد و امضا، تابع `Hack` را برایش سریعتر کنید. با توجه به این که تابع `Retrieve` تابع ایمنی در مقابل همروندی است، میخواهیم از امکانات همروندی *Go* استفاده کنیم تا زمان اجرای تابع `Hack` کمینه شود.
# نکات
+ در این سؤال حق استفاده از پکیج `runtime` زبان *Go* را ندارید.
+ داوری این سؤال ممکن است اندکی بیشتر از سایر سؤالات طول بکشد.
+ تابع `Retrieve` را در فایل ارسالی قرار ندهید.
+ شما تنها مجاز به اعمال تغییرات در فایل `main.go` هستید.
+ در صورت نیاز، میتوانید توابع دیگری نیز در فایل `main.go` تعریف کنید.
# آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، فایل `main.go` را آپلود کنید.
آرش قصد دارد یک وبسرویس خفن پیادهسازی کند، اما او میداند که از همان ابتدای کار، تعداد درخواستهای کاربران به *endpoint* هایی که بار پردازشی بالایی دارند بسیار زیاد خواهد بود. بنابراین او تصمیم گرفته که از *rate limiter* استفاده کند. از آنجایی که او نمیداند این *rate limiter* را چگونه پیادهسازی کند، از شما خواسته تا آن را برایش پیادهسازی کنید.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/35060/download_problem_initial_project/126061/) دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
```
rate-limit
├── db
│ └── connection.go
├── handlers
│ └── root.go
├── limiters
│ ├── by_app_key.go
│ └── by_ip.go
├── test
│ └── ratelimit_sample_test.go
├── go.mod
├── go.sum
└── main.go
```
%align_right_start%
وابستگی [`time/rate`](https://pkg.go.dev/golang.org/x/time/rate) در پروژه تعریف شده و برای پیادهسازی *rate limiter* باید از آن استفاده کنید.
%align_end%
## پایگاه داده
در این پروژه از پایگاه دادهی *PostgreSQL* استفاده شده است. برنامه شامل جدولی با نام `app_keys` است که شامل دو ستون `id` از نوع `BIGSERIAL` و `key` از نوع `VARCHAR(255)` است.
در پکیج `db` تابعی با نام `GetConnection` تعریف شده که باید از آن برای دریافت کانکشن دیتابیس استفاده کنید. میتوانید اطلاعات اتصال به دیتابیس را در این فایل تغییر دهید.
## روتها
تنها یک روت با آدرس `/` در برنامه تعریف شده است که میتوان *rate limiter* های مختلف را روی *handler* آن اعمال کرد.
## تابع `ByIp`
این تابع بهترتیب شامل سه پارامتر زیر است:
+ `next` از نوع `http.Handler`: همان *handler* اصلی روت است.
+ `refillRate` از نوع `rate.Limit`: میزان افزایش تعداد درخواستهای مجاز کاربر در هر ثانیه (برای مثال اگر مقدار آن برابر با ۳ باشد، در هر ثانیه، ۳ واحد به تعداد درخواستهای مجاز کاربر اضافه میشود، به شرطی که تعداد درخواستهای مجاز فعلیاش کوچکتر از `tokenBucketSize` باشد.)
+ `tokenBucketSize` از نوع `int`: سقف تعداد درخواستهای پیاپی کاربر
این تابع را طوری پیادهسازی کنید که درخواستها را براساس آیپی محدود کند. در صورتی که کاربر با محدودیت مواجه نشده باشد، پردازش درخواست باید توسط `next` صورت گیرد. در غیر اینصورت، کد پاسخ باید به `429` تغییر کند، مقدار هدر `Content-Type` باید برابر با `application/json` قرار داده شود و بدنهی پاسخ بهصورت زیر باشد:
```json
{"error": "too many requests"}
```
## تابع `ByAppKey`
امضای این تابع مشابه تابع `ByIp` است.
این تابع را طوری پیادهسازی کنید که درخواستها را براساس مقدار هدر `X-App-Key` موجود در درخواست محدود کند. اگر مقدار این هدر در جدول `app_keys` وجود نداشته باشد، محدودیت نباید اعمال شود. در غیر اینصورت، محدودیت باید اعمال شود. اگر کاربر با محدودیت مواجه شده باشد، پاسخ باید مشابه پاسخ مورد انتظار برای تابع `ByIp` باشد.
**تضمین میشود** که هنگام استفاده از این تابع، هدر `X-App-Key` در درخواست موجود است.
## ترکیب *rate limiter* ها
ممکن است بخواهیم *rate limiter* ها را ترکیب کنیم. اگر از توابع `ByIp` و `ByAppKey` در کنار یکدیگر استفاده شود، محدودیت باید هم براساس آیپی و هم براساس مقدار هدر `X-App-Key` اعمال شود. مثلاً اگر مقدار `tokenBucketSize` برابر با ۵ باشد و ۵ درخواست پیاپی با آیپیهای مختلف، اما با `X-App-Key` یکسان (بهطوری که در جدول `app_keys` موجود باشد) ارسال کنیم، باید با محدودیت مواجه شویم. همچنین اگر این درخواستها را با آیپی یکسان، اما با `X-App-Key`های متفاوت (بهطوری که در جدول `app_keys` موجود باشند) ارسال کنیم، باز هم باید با محدودیت مواجه شویم.
# نکات
+ نیازی به *persistent* بودن اطلاعات مربوط به *rate limiter* ها نیست (با خاتمهی برنامه، دادههای مربوط به *rate limiter* ها میتوانند از بین بروند).
+ شما تنها مجاز به اعمال تغییرات در پوشهی `limiters` هستید.
+ در صورت نیاز، میتوانید فایلهای جدیدی در پوشهی `limiters` تعریف کنید.
# آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، پوشهی `limiters` را زیپ کرده و آپلود کنید.