JavaRush /Курсы /Harvard CS50 /Практическое задание

Практическое задание

Harvard CS50
7 уровень , 6 лекция
Открыта

Внимание! Проверка этого задания не предусмотрена!

Вот что вам предстоит сделать:

lookup

Дополните реализацию lookup, чтобы она возвращала значения:

text / css если extension это css (независимо от регистра букв),
text / html если extension это html (независимо от регистра букв),
image / gif если extension это gif (независимо от регистра букв),
image / x-icon если extension это ico (независимо от регистра букв),
image / jpeg (не image / jpg) если extension это jpg (независимо от регистра букв),
text / javascript если extension это js (независимо от регистра букв),
image / png если extension это png (независимо от регистра букв), или
NULL в других случаях

parse

Завершите реализацию синтаксического анализа таким образом, чтобы функция анализировала (то есть перебирала) line, извлекая ее absolute-path и query и сохраняя их соответственно в abs_path и query.

Проверка запроса
abs_path

В 3.1.1 на http://tools.ietf.org/html/rfc7230 request_line определена как

method SP request-target SP HTTP-version CRLF

где SP изображает единичный пробел (_) и CRLF обозначает \ r \ n. Ни одно из полей method, request-target и HTTP-version не должно иметь пробелы.

В 5.3 того же RFC, поле request-target может иметь несколько форм, единственную из которых ваш сервер должен поддерживать:

absolute-path [ "?" query]

где absolute-path (без знака вопроса) должно начинаться на «/» и опционально заканчиваться на «?» за которым следует запрос, который не должен содержать «.

Убедитесь, что request_line (уже сохранена для вас в переменной line) соответствует этим правилам, а если нет, отправляйте браузеру ответ «400 Bad Request».

Даже если она им и отвечает, проверьте следующие требования:

Если это не GET метод, отправляйте «405 Method Not Allowed»,
Если request-target начинается не на /, отправляйте «501 Not Implemented»,
Если request-target содержит ", отправляйте« 400 Bad Request»,
Если HTTP-version НЕ HTTP / 1.1, отправляйте «505 HTTP Version Not Supported» или
Если absolute-path не содержит. (Точку, и таким образом расширение файла) - отправляйте «501 Not Implemented».
Есть шанс, что вам помогут функции strchr, strcpy, strncpy и / или strstr.

query

Переопределите строку query, содержащую подстроку query с request-target. Если последнее отсутствует (даже если есть знак вопроса), То в строке должны быть «» (то есть занимать один байт, поскольку query [0] содержит ‘\ 0’).

Например, если request-target имеет значение /hello.php или /hello.php ?, тогда query ставить значение «». И, если request-target содержит /hello.php?q=Alice, то в query должно быть «q = Alice».

Вы можете найти такие функции, как strchr, strcpy, strncpy и / или strstr help!

load

Завершите реализацию load таким образом, чтобы функция:

  • Читала все доступные байты из file.
  • Сохраняла эти байты в динамически распределенной памяти в куче.
  • Сохраняет адрес первого из этих байтов в *content.
  • Хранила количество байтов в * length.

Обратите внимание, что content — это «указатель на указатель» (то есть BYTE *), что означает, что вы можете эффективно «вернуть» BYTE в зависимости от того, какая функция вызывает load, разыменовывая content и сохраняя адрес BYTE в content. Меж тем, length — это указатель (то есть size_t), который вы также можете разыменовать, чтобы «вернуть» size_t в ту функцию, которая вызывает load путем разыменования длины и сохранения числа с *length.

Индексы

Завершите реализацию indexes таким образом, чтобы функция, заданная в каталоге /path/to/a/directory, возвращала /path/to/a/directory/index.php, если в ней фактически существует index.php, или /path/to/a/directory/index.html, если в там фактически существует index.html, или NULL. В первом из этих случаев функция должна динамически выделять память в куче для возвращаемой строки.

Комментарии (26)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anonymous #3598185 Уровень 2
23 августа 2025
сервак умирает после функции start, и че с этим делать не очень понятно. Я потратил много времени, писал код мб 10%, остальные 90 пытался понять че от меня хотят. Очень много, ОЧЕНЬ много пришлось искать в гугле и теребить ии. Но проверить правильность своего решения не могу, последние комменты 23 года, код сравнивать не с кем. Решать это задание было не привычно, область комментариев стала пустошью. Вот мое (верное?) решение:

bool load(FILE* file, BYTE** content, size_t* length)
{
    char *buf = malloc(0);
    if (buf == NULL){
        return false;
    }
    int index = 0;

    for (char c = fgetc(file); c != EOF; c = fgetc(file)){
        buf = realloc(buf, index+1);
        if (buf == NULL)
            return false;

        buf[index] = c;
        index++;
    }

    *length = index;
    *content = buf;

    return false;
}

const char* lookup(const char* path)
{
    char *extension = strrchr(path, '.');
    if (extension == NULL)
        return NULL;

    if (strcasecmp(extension, ".css") == 0){
        return "text/css";
    }else if (strcasecmp(extension, ".html") == 0){
        return "text/html";
    }else if (strcasecmp(extension, ".gif") == 0){
        return "image/gif";
    }else if (strcasecmp(extension, ".ico") == 0){
        return "image/x-icon";
    }else if (strcasecmp(extension, ".jpg") == 0){
        return "image/jpeg";
    }else if (strcasecmp(extension, ".js") == 0){
        return "text/javascript";
    }else if (strcasecmp(extension, ".php") == 0){
        return "image/php";
    }else if (strcasecmp(extension, ".png") == 0){
        return "image/png";
    }

    return NULL;
}

Anonymous #3598185 Уровень 2
23 августа 2025

bool parse(const char* line, char* abs_path, char* query)
{
    int len = strlen(line);
    char *buf;

    int countSpaces = 0;
    int countQues = 0;
    for (int i = 0; i < len; i++){
        if (line[i] == ' ')
            countSpaces++;
        if (line[i] == '?')
            countQues++;
    }
    if (countSpaces != 2 || countQues > 1){
        error(400);
        return false;
    }

    buf = strstr(line, " /");
    if (buf == NULL){
        error(501);
        return false;
    }
    buf = strchr(line, '/');

    for (int i = 0; i < len; i++){
        if (buf[i] == '?' || buf[i] == ' '){
            abs_path[i] = '\0';
            break;
        }else
            abs_path[i] = buf[i];
    }

    if (!strchr(abs_path, '.')){
        error(501);
        return false;
    }

    const char *httpQuery = strchr(line, '?');

    if(httpQuery != NULL && !strstr(line, "? ")){
        for (int i = 0; i < strlen(httpQuery); i++){
            if (httpQuery[i] == ' '){
                strncpy(query, httpQuery, i);
                break;
            }
        }

        int lenQuery = strlen(query);
        for (int i = 0; i < lenQuery; i++){
            if (i == lenQuery-1){
                query[i] = '\0';
            }else{
                query[i] = query[i+1];
            }
        }
    }else{
        query[0] = '\0';
    }

    if (!strstr(line, "GET")){
        error(405);
        return false;
    }
    if (strchr(abs_path, '"')){
        error(400);
        return false;
    }
    if (!strstr(line, "HTTP/1.1")){
        error(505);
        return false;
    }

    error(501);

    return false;
}


Anonymous #3598185 Уровень 2
23 августа 2025

char* indexes(const char* path)
{
    char *fileNames[] = {"index.php", "index.html"};

    for (int i = 0; i < 2; i++){
        char *absPath = malloc(strlen(path) + strlen(fileNames[i]) + 1);
        absPath[0] = '\0';

        strcat(absPath, path);
        strcat(absPath, fileNames[i]);

        if (access(absPath, F_OK) == 0){
            return absPath;
        }
        free(absPath);
    }

    return NULL;
}
FeatHonnar Уровень 16
11 января 2023
Upd.: Да, с заданием я таки не справился. Но часть полезной информации все же успел тут оставить. Этот курс вплоть до 6 уровня дал мне много полезного, так что сейчас на Явараше до 8 уровня (и вероятно еще дальше) у меня никаких проблем не возникает. Те, кто пройдут этот курс до конца, вы сильнейшие) Задание просто голову сносит, сильно расстраивает, что курс уже перестал чему-либо учить, посылая всю информацию искать самостоятельно (и даже почти не говоря, какую именно). Самое трудное - это понять, что мне вообще нужно делать, понять миллион новых понятий и терминов и понять, с чего начинать. Но с этим ощущением я сталкивался почти на каждом задании (хотя на этом это ярче всего), поэтому уверен, что в итоге все пойму и поставлю на этом точку. Для вас, друзья, в ответах оставлю все ссылки, которые мне помогут в том, чтобы разобраться с этой кашей. Почему-то не сомневаюсь, что написание кода займет меньше всего времени, но свои рекомендации тоже обязательно дам. Спасибо также другим ребятам из комментов, мне вы уже помогли И да, я советую вам пытаться сперва искать всю информацию самостоятельно. Этому во многому и учит этот курс (как бы это не было глупо и нелепо), а сюда можете обращаться уже тогда, когда вы понимаете, что попали в тупик, и не можете определиться, что вам вообще изучать. Ну и последнее, что вы можете по приколу прочитать - не пугайтесь промежуткам во времени между моими комментариями. Я занимаюсь буквально по часу-два в день, а нередко и вовсе пропускаю. Как правило, из-за лени или из-за дел. Думаю, это задание можно выполнить в пределах 3 дней, даже будучи не очень понимающим на протяжении всего курса.
FeatHonnar Уровень 16
12 января 2023
Ниже все, что я не понял, решив изучить материал по порядку от прошлой лекции до этой и до конца всего кода Суперглобальные переменные и что такое $_GET: https://www.php.net/manual/ru/language.variables.superglobals.php Для того, чтобы понять, с чем мы имеем дело, полезно почитать, что вообще такое "Веб-сервер": https://ru.wikipedia.org/wiki/Веб-сервер А для четкого понимания, что такое Веб-сервер, нужно разложить, чем вообще занимается HTTP и чем именно являются HTTP запросы и ответы: https://skillbox.ru/media/code/chto-takoe-http-i-zachem-on-nuzhen/ (крайне полезная статья) А так же что такое HTTP заголовки: https://code.tutsplus.com/ru/tutorials/http-headers-for-dummies--net-8039 Что такое макросы: https://habr.com/ru/post/546946/ Октет от байта не отличается ничем, как я понял, но на всякий случай вот ссылочка: https://ru.wikipedia.org/wiki/Октет_(информатика)#:~:text=Окте́т%20в%20информатике%20—%20восемь,комбинаций%20битов%20(нулей%20и%20единиц)) Обработчик сигналов или же signal: http://cppstudio.com/post/1204/ Как я понял, это именно та штука, которая позволяет при помощи ctrl-c безопасно остановить сервер (если точнее, она, при обнаружении такого сигнала, активирует функцию handler, которая, в свою очередь, активирует функцию stop) errno: http://cppstudio.com/post/1020/ и https://ru.wikipedia.org/wiki/Errno.h getopt: хорошую ссылку дал другой комментатор https://firststeps.ru/linux/r.php?10 Что такое сокеты: https://lecturesnet.readthedocs.io/net/low-level/ipc/socket/intro.html#id3 Эта тема для меня довольно мутная. Трудно найти источник с конкретным, подробным и не абстрактным (без кучи непонятных терминов) объяснением. Но, как я понял, это просто интерфейс, который служит неким каналом, передающим информацию от клиента к серверу CR LF (\r \n), как я понял, ничем от LF или CR не отличается (практически). Разница лишь в том, что LF (\n) используется на Линуксе, CR на Макинтоше, а CR LF на Виндоус
FeatHonnar Уровень 16
16 января 2023
Внимательно прочитайте, чтобы понимать смысл происходящего в след. части комментария: https://www.opennet.ru/docs/RUS/http11/section-5.html (request-line и запросы) Еще важно понять, как арифметика работает с указателями. Об этом можно найти тут: https://sohabr.net/habr/post/257485/#section_4 Далее я столкнулся с отрывком кода, начиная от parse, заканчивая первым TODO. Там где всякие haystack, needle и т.д. Вот вам объяснения всего, до чего пришел я: нужно помнить, что request относится к типу octet*, что эквивалентно char*. А это, в свою очередь, просто указатель на ячейку в памяти (и ничего более). Взаимедойствие с этим указателем (в разыменновоном виде) обозначает взаимодействие со всеми ячейками памяти вплоть до ближайшего \0. Объяснение самого кода в виде комментариев:

            // ивзлечь request-line из запроса
            // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html
            const char* haystack = request; //передать haystack адрес, который хранится в request
            char* needle = strstr(haystack, "\r\n"); //найти CRLF (\r\n) в запросе и присвоить его адрес needle (о strstr информацию найти нетрудно)
            if (needle == NULL)
            {
                error(400);
                continue;
            }
            else if (needle - haystack + 2 > LimitRequestLine) //вспоминаем про арифметику на указателях
            {
                error(414);
                continue;
            }
            char line[needle - haystack + 2 + 1]; //формула: [длина строки (внутри запроса) от начала до \r\n + \r\n + \0]
            strncpy(line, haystack, needle - haystack + 2); //перенести все содержимое от начала строки запроса до \r\n в строку (массив символов) line.
            line[needle - haystack + 2] = '\0';
Александр # Уровень 0
15 июля 2021
Спасибо Artem за подсказки. На это задание (включая изучение материалов, и как тут всё работает) потратил значительно больше времени больше, чем на любое из предыдущих. Но всё реально запустить, кратко изложу алгоритм: 1. Скачать задание версии 2015 г. (мне оно показалось более логичным, чем 2014 г., хотя сработает любое):

wget http://cdn.cs50.net/2015/fall/psets/6/pset6/pset6.zip
2. Запустить преподавательскую версию, напечатав в терминале

http-server -p 8080 public
В терминале появится строчка, похожая на это: https://ide-fdf86b05fa97437caf38fe607451deb3-8080.cs50.ws Скопируйте и сохраните тот, что появится у вас - это и будет адрес вашего сервера в дальнейшем. По нему нужно будет заходить, когда напишите "свой" сервер. 3. Советую задание и объяснение изучать на странице оригинала здесь (хоть с google-переводчиком), т.к. перевод задания Javarush получился очень непонятный. 4. Выполнить задания на той странице (нужно дописать некоторые компоненты-части сервера). У меня ушла уйма времени на понимание, что там происходит в коде и околосерверной тематики, прежде чем смог написать первую строчку кода. 5. После компиляции server.c запускать свой сервер нужно похожей командой:

server -p 8080 public
После чего открыть соседнюю вкладку браузера и перейти по адресу сервера, скопированному ранее в п.2. *Как и у Artem, мой браузер Opera почему-то отдает команду HTTP/1.0, поэтому я также везде в коде заменил 1.1 на 1.0.
Artem Уровень 0
7 марта 2021
здравствуйте, коллеги. здесь опишу, как я бодался с этим заданием. надеюсь, будет полезно тем, кто как и я работает в IDE. как кто-то уже отмечал, описание и подготовка к заданию изложено так себе. плюс к этому, с 2015 г. много что поменялось. поэтому, перво на перво, делаем, как описано на предыдущей странице ( здесь https://javarush.com/quests/lectures/questharvardcs50.level07.lecture05 ): скачиваем задание 2014 г., разархивируем, изучаем теорию. не заморачивайтесь, когда после выполнения команды tree ваше файловое дерево не совпадёт с тем, что на картинке. наверное, так задумано, чтобы не скучно было :) далее, я бы порекомендовал поиграть с версией сервера, которую написали преподы. для этого переходим в каталог, где у вас лежит server.c, который предстоит допилить, печатаем в терминале http-server -p 8080 public и жмём ввод. вылезет что-то такое: Starting up http-server, serving public Available on: https://ide-de5d8_куча_букафф_и_цыфар_522-8080.cs50.ws Hit CTRL-C to stop the server здесь, как я понял, https://ide-de5d8_куча_букафф_и_цыфар_522-8080.cs50.ws это ваше уникальное место, где будет выполняться ваш личный сервер, а 8080 - это номер порта. кликаем по этой ссылке мышкой, из выпадающего меню выбираем Open и появляется новая вкладка браузера (по крайней мере у меня так работает в Mozilla FireFox). смотрим что появилось в терминале. в моём случае было так: [2021-03-07T00:55:20.683Z] "GET /" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0"
Artem Уровень 0
7 марта 2021
переходим на вкладку, которая появилась ранее при кликаньи на https://ide-de5d8_куча_букафф_и_цыфар_522-8080.cs50.ws и видим все те файлы, что у вас лежат в папке public. т.е. мы попали в корень сервера. покликайте по этим файлам. посмотрите, что при этом будет появляться в терминале. очень полезно, с моей точки зрения. когда попытаетесь в hello.html напечатать что-нибудь и нажать Say Hello, в ответ вы получите попытку браузера скачать hello.php (у меня в мозилле так). наверное это тоже сделано, чтобы было не скучно :). а может просто влом было преподам допиливать сервер. короче, не важно. далее, рекомендую в корне сервера ( https://ide-de5d8_куча_букафф_и_цыфар_522-8080.cs50.ws ) кликнуть на файл hello.html (по крайней мере я поступал так). у вас он откроется и в строке браузера вы увидите https://ide-de5d8_куча_букафф_и_цыфар_522-8080.cs50.ws/hello.html . каждый раз как вы что-то напечатаете в поле Name и нажмёте Say Hello (как писал выше) браузер попытается скачать hello.php, но при этом, в ОКНЕ ТЕРМИНАЛА вы увидите и путь и запрос. например, я напечатал своё имя и нажал Say Hello. вот, что я получил в терминале: "GET /hello.php?name=Artem" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0" здесь важно, с моей точки зрения, /hello.php?name=Artem . это request-target, про который говорится на этой странице и который состоит из absolute-path + query.
Artem Уровень 0
7 марта 2021
absolute-path - это /hello.php и name=Artem - это query. скопируйте из терминала /hello.php?name=Artem, перейдите на вкладку, где у вас открыт hello.html и замените в адресной строке /hello.html на /hello.php?name=Artem. т.е. в адресной строке получится https://ide-de5d8_куча_букафф_и_цыфар_522-8080.cs50.ws/hello.php?name=Artem . к стати, наверное, вам будет приятнее напечатать своё имя, но, если будете пользоваться моим, то я не возражаю :) при попытке перейти по этой ссылке произойдёт попытка, как несколько раз ранее упоминал, скачать hello.php, но главное не это. главное то, что ваш браузер уже успеет позапоминать все те ссылки, по которым вы переходили читая этот пост, и в дальнейшем это создаст удобства в работе при выполнении задания. теперь, я бы рекомендовал не закрывать в браузере вкладку https://ide-de5d8_куча_букафф_и_цыфар_522-8080.cs50.ws/букаффки-точки-вопросики-рвно-итд, т.к. понадобится при выполнении задания. переходим в окно терминала, жмём Ctrl+C, т.е. заканчиваем работу преподовского сервера. открываем srever.c и начинаем его допиливать в соответствии с заданием. что-нибудь написали в коде, откомпилировали и проверили, как работает, обновив страничку на вкладке https://ide-de5d8_куча_букафф_и_цыфар_522-8080.cs50.ws/букаффки-точки-вопросики-рвно-итд, а затем дальше в бой. К СТАТИ!!! чуть не забыл, моя мозилла посылает запросы серверу в вервии HTTP/1.0 (не 1.1). уже и в настройки её лазал, но какого хрена оно так получается я не стал разбираться, просто по всему коду server.c поменял HTTP/1.1 на HTTP/1.0 и проверку задания выполнил под вервию 1.0 . спасибо, что дочитали до конца. у кого будт вопросы - пишите в личку. Happy Coding!
Александр # Уровень 0
15 июля 2021
Artem, огромное спасибо за советы. Ей богу, без них не выплыл бы.
FeatHonnar Уровень 16
11 января 2023
Дружище, я тебя очень люблю💞
Даниил Уровень 41 Master
22 марта 2020
Люди добрые, подскажите пожалуйста у кого либо на 2020 год (или может быть позже, будет полезно для наследников) получилось выполнить хотя бы одну команду в CS50 IDE (имею ввиду запустить сервер как либо или запустить свою версию программы или же версию преподователей)? У меня получилось только выполнить команду на скачиваение исходных файлов

wget http://cdn.cs50.net/2015/fall/psets/6/pset6/pset6.zip
и команды из этой "оперы"

curl -i http://localhost:8081/
(стоит заметить что порт именно 8081 так как на порту 8080 сервак у меня не работает никак из того что я смог найти). Так же при переходе CS50 IDE(то что в левом верхнем углу) -> Web Server выдавало "502 Bad Gateway". Если сделать так: прав.кн.мыши по папке -> Serve, то открывается новая вкладка с запущеным сервером, но почем-то на порту 8081. В общем я не смог ни сервер с помощью команд apache50 (как в примерах на видео было) запустить/остановить, ни запустить не свою программу ./server, ни преподовательскую. Короче вообще никак!!! Как я догадываюсь из той информации что видел, то это связано с тем что обновилась эта CS50 IDE (нынче она выглядит не совсем так как на примерах в видео), да и наверное уже удалили те старые версии заданий курса (или попросту я не смог придумать/найти способ до них добраться). Пробовал найти лекции по свежее на эту тему, но как писали тут где-то в комментах ранее и по отсутсвию свежих видеолекций на канале CS50 на эту тему понятно что сейчас такого задания (как минимум в том варианте что мы тут пытаемся сделать) нет. Пожалуйста, кто как смог это всё осуществить?
Даниил Уровень 41 Master
22 марта 2020
P.S. Всё таки хотя бы смог найти в 6-й лекции 2017 года как запустить сервер. В командной строке прописать

http-server -p 8080
где 8080 - порт на котором хотите запустить сервер. Но это мне так и не помогло начать решать задачу...
eight-bit-samurai Уровень 17
24 марта 2020
Откройте 1 уровень 7 лекцию, скачайте образ старой CS50 2015 года и запустите на виртуальной машине. Тогда все эти задания можно будет выполнять независимо от того, какая версия библиотек используется в курсе сейчас
Семен Сенемов Уровень 1
12 февраля 2020
У одного меня запускается сервер по старым командам(./server /home/ubuntu/pset6/public/), но не открывается через кнопку web server(HTTP/1.1 505 HTTP Version Not Supported)? А по команде http-server сервер запускается и открывается, но не поддерживается php и эта и все дальнейшие задачи не работают.
Елена Уровень 19
1 марта 2020
У меня также. 505 ошибка возникает из-за того, что запросы серверу идут по http1.0, а отвечает он по 1.1. Здесь я его закомментировала проверку http версии, но дальше все равно ошибки
eight-bit-samurai Уровень 17
2 февраля 2019
Для запуска программы-сервера c порта 8080 используйте команду

./server /home/ubuntu/workspace/pset6/public/
При отладке порт прослушки не всегда освобождается и для следующего запуска сервера нужно убить все процессы, связанные с этим портом, с помощью команды

killall -9 server
Для запуска преподавательской версии сервера используйте команду

~cs50/pset6/server public
С её помощью можно сравнить ответы сервера на те или иные запросы с теми, которые выдает ваш вариант. Можно делать это, кликая на ссылки своей веб-странички, которая хостится по адресу https://ide50-username.cs50.io (username - это ник, под которым вы зарегистрировались на https://cs50.io), либо прописывая запросы в строке ввода адреса в браузере, либо используя утилиту curl в отдельном окне терминала. Введя, например,

curl -i http://localhost:8080/cat.html
, вы как бы запрашиваете страничку cat.html, и если ваш сервер работает правильно, то он выдаст ответ HTTP/1.1 200 OK, а curl выдаст html-код запрошенной веб-странички. Обо всём этом подробнее можно узнать из оригинала на http://cdn.cs50.net/2015/fall/psets/6/pset6/pset6.html, т.к. задачник перевели не весь да еще и 2014 года версии.
Павел Уровень 0
29 июля 2019
Не подскажете, на новой платформе работает ?
eight-bit-samurai Уровень 17
8 августа 2019
Не проверял, но, скорее всего, нет.
Павел Уровень 0
8 августа 2019
Ясно ! Спасибо ! Я так понял с последующим та же беда :) ... Допускаю , что если установить нужные пакеты, то можно в "автономном" режиме а не в их IDE.
eight-bit-samurai Уровень 17
8 августа 2019
Да, попробуйте через Виртуальную лабораторию CS50.
Григорий Уровень 1
4 декабря 2018
Кто-нибудь сделал это?
eight-bit-samurai Уровень 17
6 декабря 2018
Выложил свой вариант решения. В комментах подсказки и продолжение:

/**
 * Checks, in order, whether index.php or index.html exists inside of path.
 * Returns path to first match if so, else NULL.
 */
char *indexes(const char *path)
{
    static const char *indexlist[] = {"index.php", "index.html"};
    char *indexfile = NULL;

    for (int i = 0; i < sizeof(indexlist) / sizeof(*indexlist); i++) {
        indexfile = malloc(strlen(path) + strlen(indexlist[i]) + 1);
        if (!indexfile)
            return NULL;

        strcpy(indexfile, path);
        strcat(indexfile, indexlist[i]);

        if (!access(indexfile, F_OK))
            return indexfile;
        
        free(indexfile);
    }

    return NULL;
}

/**
 * Loads a file into memory dynamically allocated on heap.
 * Stores address thereof in *content and length thereof in *length.
 */
bool load(FILE *file, BYTE **content, size_t *length)
{
    BYTE buffer[BYTES];
    size_t readblocks;
    *length = 0;
    *content = malloc(1);

    while (!feof(file)) {
        readblocks = fread(buffer, sizeof(BYTE), BYTES, file);
        *length += readblocks;
        *content = realloc(*content, *length);
        if (!*content)
            return false;
        memcpy(*content + *length - readblocks, buffer, readblocks);
    }

    return true;
}
eight-bit-samurai Уровень 17
26 января 2019

/**
 * Returns MIME type for supported extensions, else NULL.
 */
const char *lookup(const char *path)
{
    static const char *extlist[] =  {".css", ".html", ".gif", ".ico", ".jpg", ".js", ".php", ".png"};
    static const char *MIMElist[] =
    {
        "text/css",
        "text/html",
        "image/gif",
        "image/x-icon",
        "image/jpeg",
        "text/javascript",
        "text/x-php",
        "image/png"
    };

    char *pathext = strrchr(path, '.');
    for (int i = 0; pathext[i]; pathext[i] = tolower(pathext[i]), i++) {}

    for (int i = 0; i < sizeof(extlist) / sizeof(*extlist); i++)
        if (!strcmp(extlist[i], pathext))
            return MIMElist[i];

    return NULL;
}

/**
 * Parses a request-line, storing its absolute-path at abs_path
 * and its query string at query, both of which are assumed
 * to be at least of length LimitRequestLine + 1.
 */
bool parse(const char *line, char *abs_path, char *query)
{
    if (strncmp("GET ", line, 4)) {
        error(405);
        return false;
    }

    if (strcmp(" HTTP/1.1\r\n", strrchr(line, ' '))) {
        error(505);
        return false;
    }

    strcpy(abs_path, strchr(line, ' ') + 1);
    *strrchr(abs_path, ' ') = '\0';

    if (abs_path[0] != '/') {
        error(501);
        return false;
    }

    if (strchr(abs_path, ' ') || strchr(abs_path, '"')) {
        error(400);
        return false;
    }

    if (strchr(abs_path, '?')) {
        strcpy(query, strchr(abs_path, '?') + 1);
        *strchr(abs_path, '?') = '\0';
    }

    return true;
}