Страница 1 из 1

обработка куки для веб-сервера

Добавлено: 17 авг 2024, 17:40
ya
Чтобы добавить логику для работы с куками в веб-сервер на C++, нужно сделать несколько вещей:
1. Считать куки из заголовка запроса.
2. Сравнить их с уже имеющимися значениями.
3. Отправить их обратно клиенту, если это необходимо.

Ниже приведу код, который добавляет такую функциональность:

Код: Выделить всё

#include <iostream>
#include <string>
#include <cstring>
#include <sstream>
#include <unistd.h>
#include <arpa/inet.h>
#include <map>

const int PORT = 8070;
const int BACKLOG = 10;
const int BUFFER_SIZE = 1024;

// Хранение значений куки в переменной
std::map<std::string, std::string> existingCookies = {
    {"cookie1", "value1"},
    {"cookie2", "value2"}
};

std::map<std::string, std::string> parseData(const std::string& data) {
    std::map<std::string, std::string> result;
    std::string keyValuePair;
    std::istringstream stream(data);

    while (std::getline(stream, keyValuePair, '&')) {
        size_t pos = keyValuePair.find('=');
        if (pos != std::string::npos) {
            std::string key = keyValuePair.substr(0, pos);
            std::string value = keyValuePair.substr(pos + 1);
            result[key] = value;
        }
    }

    return result;
}

// Функция для получения значений куки из заголовка
std::map<std::string, std::string> getCookies(const std::string& request) {
    std::map<std::string, std::string> cookies;
    std::string line;
    std::istringstream request_stream(request);

    while (std::getline(request_stream, line)) {
        if (line.find("Cookie: ") == 0) {
            size_t pos = line.find("Cookie: ") + 8; // Начало значений куки
            std::string cookieData = line.substr(pos);
            cookies = parseData(cookieData);
            break;
        }
    }

    return cookies;
}

std::string handleGET(const std::string& path) {
    std::string body = "<!DOCTYPE html>"
        "<html lang='en'>"
        "<head>"
        "<meta charset='UTF-8'>"
        "<meta name='viewport' content='width=device-width, initial-scale=1.0'>"
        "<title>Простой C++ Веб-сервер</title>"
        "</head>"
        "<body>"
        "<h1>Вы сделали GET запрос на путь: " + path + "</h1>"
        "<form action='/' method='POST'>"
        "<input type='text' name='send_text'>"
        "<br>"
        "<input type='text' name='send_text222'>"
        "<input type='submit' value='Submit'>"
        "</form>"
        "</body>"
        "</html>";
    
    return "HTTP/1.1 200 OK
"
           "Content-Type: text/html; charset=utf-8
"
           "Content-Length: " + std::to_string(body.size()) + "\n"
           "Connection: close
"
           "\n" + body; 
}

std::string handlePOST(const std::string& data, const std::map<std::string, std::string>& cookies) {
    std::string body = "<!DOCTYPE html>"
        "<html lang='en'>"
        "<head>"
        "<meta charset='UTF-8'>"
        "<meta name='viewport' content='width=device-width, initial-scale=1.0'>"
        "<title>Простой C++ Веб-сервер</title>"
        "</head>"
        "<body>"
        "<h1>Вы сделали POST запрос с данными:</h1>"
        "<pre>" + data + "</pre>"
        "<h2>Значения куки:</h2><pre>";

    // Сравнение куки
    for (const auto& existing : existingCookies) {
        body += existing.first + ": " + existing.second + " | ";
    }
    body += "</pre></body></html>";

    return "HTTP/1.1 200 OK
"
           "Content-Type: text/html; charset=utf-8
"
           "Content-Length: " + std::to_string(body.size()) + "\n"
           "Connection: close
"
           "\n" + body; 
}

std::string parseRequest(const std::string& request) {
    std::string method, path, data;
    std::istringstream request_stream(request);
    request_stream >> method >> path; // Получаем метод и путь

    std::string line;
    std::map<std::string, std::string> cookies;

    while (std::getline(request_stream, line) && line != "") {
        // Собираем заголовки и получаем куки
        if (line.find("Cookie: ") == 0) {
            cookies = getCookies(request);
        }
    }

    // Если это POST запрос, то нужно получить данные
    if (method == "POST") {
        // Получаем длину содержимого
        while (std::getline(request_stream, line)) {
            if (line.find("Content-Length: ") != std::string::npos) {
                int content_length = std::stoi(line.substr(16));
                data.resize(content_length);
                request_stream.read(&data[0], content_length);
                break;
            }
        }
        
        return handlePOST(data, cookies);
    } else if (method == "GET") {
        return handleGET(path);
    }

    return "HTTP/1.1 405 Method Not Allowed
"
           "Connection: close
"
           "\n";
}

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }
    
    if (listen(server_fd, BACKLOG) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    std::cout << "Server is listening on port " << PORT << std::endl;

    while (true) {
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("Accept failed");
            exit(EXIT_FAILURE);
        }

        char buffer[BUFFER_SIZE] = {0};
        read(new_socket, buffer, BUFFER_SIZE);
        std::string request(buffer);
        std::cout << "Received request:\n" << request << std::endl;

        std::string response = parseRequest(request);
        send(new_socket, response.c_str(), response.size(), 0);
        close(new_socket);
    }

    return 0;
}
Изменения и дополнения:

1. Структура `existingCookies`: Хранит известные значения куки для сравнения.
2. Функция `getCookies`: Извлекает куки из заголовков запроса.
3. Изменение функции `handlePOST`: Теперь она отображает значения куки, которые были сохранены на сервере.
4. Сравнение куки: Сопоставляет полученные куки с уже существующими и выводит их.

Теперь, когда клиент отправляет HTTP-запросы к серверу, они будут получать как данные формы, так и значения куки, которые были сохранены на сервере.

Re: обработка куки для веб-сервера

Добавлено: 17 авг 2024, 18:15
ya
Для установки куки на клиенте, необходимо добавить соответствующий заголовок в HTTP-ответ, который сервер отправляет в ответ на запросы от клиента. Если вы хотите, чтобы клиент получил куки после выполнения определенного запроса (GET или POST), вы должны добавить заголовок Set-Cookie.

Код: Выделить всё

std::string handleGET(const std::string& path) {
    std::string body = "<!DOCTYPE html>"
        "<html lang='en'>"
        "<head>"
        "<meta charset='UTF-8'>"
        "<meta name='viewport' content='width=device-width, initial-scale=1.0'>"
        "<title>Простой C++ Веб-сервер</title>"
        "</head>"
        "<body>"
        "<h1>Вы сделали GET запрос на путь: " + path + "</h1>"
        "<form action='/' method='POST'>"
        "<input type='text' name='send_text'>"
        "<br>"
        "<input type='text' name='send_text222'>"
        "<input type='submit' value='Submit'>"
        "</form>"
        "</body>"
        "</html>";

    return "HTTP/1.1 200 OK
"
           "Content-Type: text/html; charset=utf-8
"
           "Set-Cookie: my_cookie=cookie_value; Path=/; HttpOnly
"
           "Content-Length: " + std::to_string(body.size()) + "\n"
           "Connection: close
"
           "\n" + body; 
}

std::string handlePOST(const std::string& data) {
    std::string body = "<!DOCTYPE html>"
        "<html lang='en'>"
        "<head>"
        "<meta charset='UTF-8'>"
        "<meta name='viewport' content='width=device-width, initial-scale=1.0'>"
        "<title>Простой C++ Веб-сервер</title>"
        "</head>"
        "<body>"
        "<h1>Вы сделали POST запрос с данными:</h1>"
        "<pre>" + data + "</pre>"
        "</body>"
        "</html>";

    return "HTTP/1.1 200 OK
"
           "Content-Type: text/html; charset=utf-8
"
           "Set-Cookie: my_cookie=cookie_value; Path=/; HttpOnly
"
           "Content-Length: " + std::to_string(body.size()) + "\n"
           "Connection: close
"
           "\n" + body; 
}

добавлен заголовок Set-Cookie, который будет отправлен клиенту вместе с HTTP-ответом.

Пояснение о заголовках:
Set-Cookie: Этот заголовок используется для установки куки. Вы можете изменить имя и значение куки по своему усмотрению.
Path=/: Это определяет, для каких URL-адресов кука будет действовать. В данном случае кука будет доступна для всех URL-адресов вашего сервера.
HttpOnly: Этот флаг предотвращает доступ к куки из JavaScript, что помогает защитить куки от некоторых атак.

Re: обработка куки для веб-сервера

Добавлено: 20 авг 2024, 18:16
ya
В C++ std::map позволяет вам хранить пары "ключ-значение", и доступ к значению по ключу выполняется с помощью итераторов или методов класса std::map. Если у вас есть переменная cookies типа std::map<std::string, std::string>, и вы хотите получить значение по ключу, вы можете сделать это следующим образом:

Проверка наличия ключа: Прежде чем получить значение, имеет смысл проверить, действительно ли существует ключ в мапе, чтобы избежать ненужных ошибок.
Получение значения по ключу: Вы можете использовать оператор [] или метод at(), чтобы получить значение.
Вот пример, который демонстрирует эти шаги:

Код: Выделить всё

#include <iostream>
#include <map>
#include <string>

int main() {
    // Создание карты cookies
    std::map<std::string, std::string> cookies;
    cookies["session_id"] = "abc123";
    cookies["user_id"] = "user456";

    std::string key = "session_id";

    // Проверка наличия ключа и получение значения
    if (cookies.find(key) != cookies.end()) {
        std::string value = cookies[key]; // или cookies.at(key);
        std::cout << "Значение по ключу '" << key << "': " << value << std::endl;
    } else {
        std::cout << "Ключ '" << key << "' не найден!" << std::endl;
    }

    return 0;
}
Мы создаем std::map, содержащую cookies.
С помощью метода find() мы проверяем, существует ли ключ в мапе. Метод find() возвращает итератор на элемент, если он найден, или end() в противном случае.
Если ключ существует, мы можем получить его значение, используя оператор [] или метод at(). Обратите внимание, что если вы используете [], он добавит ключ с пустым значением, если ключ не существует, в то время как at() выбросит исключение std::out_of_range.
Наконец, мы выводим найденное значение.
Этот подход позволит вам гибко управлять парами ключ-значение, используя std::map.