diff --git a/stress-test/Makefile b/stress-test/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..bf775847ec8b46116b6435e5c9fc13b7bb6e7f92 --- /dev/null +++ b/stress-test/Makefile @@ -0,0 +1,12 @@ +PROJECT:=client +OBJS:=client.o +CFLAGS:=-Wall -std=gnu11 -ggdb +LDFLAGS:=-lcurl -ltidy + +all: client + +%.o: %.c + gcc $< -c $(CFLAGS) + +$PROJECT: $(OBJS) + gcc $@-o $$ $(LDFLAGS) diff --git a/stress-test/client.c b/stress-test/client.c new file mode 100644 index 0000000000000000000000000000000000000000..cea43bc72fd6374bed6f86edecd1c38f7aea892b --- /dev/null +++ b/stress-test/client.c @@ -0,0 +1,325 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <curl/curl.h> +#include <tidy/tidy.h> +#include <tidy/tidybuffio.h> +#include <time.h> +#include "config.h" + +struct memory { + char *response; + size_t size; +}; + +struct submit_action { + int contest_id; + int task_id; + char *local_file; + char *reported_filename; +}; + +static size_t write_callback(void *data, size_t size, size_t nmemb, void *userp) { + size_t realsize = size *nmemb; + struct memory *mem = (struct memory *)userp; + + char *ptr = realloc(mem->response, mem->size + realsize + 1); + if (ptr == NULL) + return 0; + + mem->response = ptr; + memcpy(&(mem->response[mem->size]), data, realsize); + mem->size += realsize; + mem->response[mem->size] = 0; + + return realsize; +} + +static void debug_print_cookies(CURL *curl) { + CURLcode res; + struct curl_slist *cookies; + struct curl_slist *nc; + int i; + + printf("Cookies, curl knows:\n"); + res = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies); + if (res != CURLE_OK) { + fprintf(stderr, "Curl curl_easy_getinfo failed: %s\n", curl_easy_strerror(res)); + return; + } + nc = cookies; + i = 0; + while (nc) { + printf("[%d]: %s\n", i, nc->data); + nc = nc->next; + i++; + } + if (i == 0) { + printf("(none)\n"); + } + curl_slist_free_all(cookies); +} + + +void setup_curl(CURL *curl, struct memory *chunk) { + chunk->response = NULL; + chunk->size = 0; + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // skip cert verification + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // skip hostname verification (is it measureably faster?) + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); +} + +void free_body(struct memory *chunk) { + free(chunk->response); + chunk->response = NULL; + chunk->size=0; +} + +void print_body(CURL *curl, struct memory *chunk) { + printf("%li\n", chunk->size); + printf("BEGIN >>>>>>\n"); + for (int i = 0; i < chunk->size; i++) { + printf("%c", chunk->response[i]); + } + printf("\n<<<<<< END\n"); +} + +void perform_get(CURL *curl, char *url) { + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, url); + + res = curl_easy_perform(curl); + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); +} + +void search_csrf(TidyDoc doc, TidyNode tnod, char **csrf) { + TidyNode child; + for (child = tidyGetChild(tnod); child; child = tidyGetNext(child) ) { + ctmbstr name = tidyNodeGetName(child); + if (name) { + // It is HTML tag + TidyAttr attr; + + bool it_is_csrf = false; + const char *it_has_value = NULL; + for (attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr) ) { + if (!strcmp(tidyAttrName(attr), "id")) { + if (!strcmp(tidyAttrValue(attr), "csrf_token")) { + it_is_csrf = true; + } + } + if (!strcmp(tidyAttrName(attr), "value")) { + it_has_value = tidyAttrValue(attr); + } + } + if (it_is_csrf && it_has_value) { + //printf("Yay! csrf token = %s\n", it_has_value); + if (*csrf) + free(*csrf); + *csrf = strdup(it_has_value); + } + } + search_csrf(doc, child, csrf); + } +} + +void extract_csrf(struct memory *chunk, char **csrf) { + TidyDoc tdoc; + TidyBuffer docbuf = {0}; + TidyBuffer tidy_errbuf = {0}; + + tdoc = tidyCreate(); + + tidySetErrorBuffer(tdoc, &tidy_errbuf); + tidyBufInit(&docbuf); + + // copy response buffer + tidyBufAppend(&docbuf, chunk->response, chunk->size); + + tidyParseBuffer(tdoc, &docbuf); // real parse + search_csrf(tdoc, tidyGetRoot(tdoc), csrf); // find element + //fprintf(stderr, "%s\n", tidy_errbuf.bp); // print errors + + tidyBufFree(&docbuf); + tidyBufFree(&tidy_errbuf); + tidyRelease(tdoc); +} + +void load_login_page (CURL *curl, struct memory *chunk, char **csrf) { + perform_get(curl, BASE_URL "/"); + free_body(chunk); + + perform_get(curl, BASE_URL "/auth/login"); + //print_body(curl, chunk); + //print_cookies(curl); + extract_csrf(chunk, csrf); + //printf("csrf token = %s\n", csrf); + free_body(chunk); +} + + +void do_login (CURL *curl, struct memory *chunk, char **csrf, char *login_email, char *login_password) { + char *enc_email = curl_easy_escape(curl, login_email, strlen(login_email)); + char *enc_password = curl_easy_escape(curl, login_password, strlen(login_password)); + char *enc_submit = curl_easy_escape(curl, "Přihlásit se", strlen("Přihlásit se")); + char *post_data; + asprintf(&post_data, "csrf_token=%s&email=%s&passwd=%s&submit=%s&next=", *csrf, enc_email, enc_password, enc_submit); + curl_free(enc_email); + curl_free(enc_password); + curl_free(enc_submit); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); + + perform_get(curl, BASE_URL "/auth/login"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + free(post_data); + free_body(chunk); +} + +void load_contest (CURL *curl, struct memory *chunk, int contest_id) { + char *url; + asprintf(&url, BASE_URL "/user/contest/%i/", contest_id); + perform_get(curl, url); + free(url); + + //print_body(curl, chunk); + free_body(chunk); +} + +void load_task (CURL *curl, struct memory *chunk, char **csrf, int contest_id, int task_id) { + char *url; + asprintf(&url, BASE_URL "/user/contest/%i/task/%i/", contest_id, task_id); + perform_get(curl, url); + free(url); + + //print_body(curl, chunk); + extract_csrf(chunk, csrf); + //printf("csrf token = %s\n", csrf); + free_body(chunk); +} + +bool submit_task (CURL *curl, struct memory *chunk, char **csrf, int contest_id, int task_id, char *local_file, char *reported_filename) { + char *url; + asprintf(&url, BASE_URL "/user/contest/%i/task/%i/", contest_id, task_id); + curl_mime *mime; + curl_mimepart *part; + + mime = curl_mime_init(curl); + + part = curl_mime_addpart(mime); + curl_mime_data(part, *csrf, CURL_ZERO_TERMINATED); + curl_mime_name(part, "csrf_token"); + + part = curl_mime_addpart(mime); + curl_mime_data(part, "Jenduv automatizovany test submit", CURL_ZERO_TERMINATED); + curl_mime_name(part, "note"); + + part = curl_mime_addpart(mime); + curl_mime_data(part, "Odevzdat", CURL_ZERO_TERMINATED); + curl_mime_name(part, "submit"); + + part = curl_mime_addpart(mime); + curl_mime_filedata(part, local_file); + curl_mime_filename(part, reported_filename); + curl_mime_name(part, "file"); + + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + curl_easy_setopt(curl, CURLOPT_URL, url); + + char errbuf[CURL_ERROR_SIZE]; + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); + errbuf[0] = 0; + + CURLcode res = curl_easy_perform(curl); + + if(res != CURLE_OK) { + size_t len = strlen(errbuf); + fprintf(stderr, "\nlibcurl: (%d) ", res); + if(len) + fprintf(stderr, "%s%s", errbuf, + ((errbuf[len - 1] != '\n') ? "\n" : "")); + else + fprintf(stderr, "%s\n", curl_easy_strerror(res)); + } + + free(url); + curl_easy_setopt(curl, CURLOPT_MIMEPOST, NULL); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_mime_free(mime); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + if (http_code != 200) { + printf("Post file failed:\n"); + print_body(curl, chunk); + free_body(chunk); + return false; + } + free_body(chunk); + return true; +} + +bool full_test(char *login_email, char *login_password, struct submit_action *actions) { + bool ret = true; + CURL *curl; + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); + + struct memory chunk; + char *csrf = NULL; + if (curl) { + setup_curl(curl, &chunk); + load_login_page(curl, &chunk, &csrf); + + do_login(curl, &chunk, &csrf, login_email, login_password); + + while (actions->contest_id) { + /* + printf("performing action %i %i %s %s\n", actions->contest_id, actions->task_id, + actions->local_file, actions->reported_filename); + */ + load_contest(curl, &chunk, actions->contest_id); + load_task(curl, &chunk,&csrf, actions->contest_id, actions->task_id); + + if (!submit_task(curl, &chunk, &csrf, + actions->contest_id, actions->task_id, + actions->local_file, actions->reported_filename)) { + ret = false; + } + + actions++; + } + + // cleanup + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + if (csrf) + free(csrf); + return ret; +} + +int main() { + char *login_email = "had+ucastnik@kam.mff.cuni.cz"; + char *login_password = "UTX0seFkVim51vkOwER5G8GjB5eEq7G9"; + + struct submit_action actions[] = { + //{ 25, 3, "/home/had/BratruvZpevnik_v4-0.pdf", "zpevnik.pdf"}, + //{ 25, 4, "testsubmit_02.pdf", "moje_reseni.pdf"}, + //{ 25, 5, "/home/had/sada3.pdf", "sendvic.pdf"}, + //{ 25, 6, "/home/had/stitek.pdf", "sken.pdf"}, + { 0, 0, NULL, NULL} + }; + + full_test(login_email, login_password, actions); + + return 0; +} diff --git a/stress-test/config.h b/stress-test/config.h new file mode 100644 index 0000000000000000000000000000000000000000..8eeb474cc81ffa06c40f1b905ccd8cd10c402861 --- /dev/null +++ b/stress-test/config.h @@ -0,0 +1,6 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define BASE_URL "https://mo.mff.cuni.cz/osmo-test" + +#endif