IBM Coding challenge 1st round

Question:-

Write a simple/basic TCP proxy CLI app.

Requirements
C++20
Use only Boost::asio
Bonus if using asio coroutines
Should work/run on at least one platform (macOS or Linux or Windows)
Should not use or do anything that prevents it from building/running on the other platforms
A single cpp file is fine (or a few separate cpp/hpp if needed)
Bonus if providing a cross-platform CMake/conan project
As small and minimal coding as necessary to get the job done

Usage
./proxy <listen_port> <target_address> <target_port>
I should be able to point this to a webserver (port 80) and then just do a curl or telnet locally (on the
listen_port) and get the content of the webpage:

./proxy 12345 www.boost.org 80
And then on a separate terminal:

curl http://localhost:12345/ should give me the same as curl http://www.boost.org/:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="https://www.boost.org/">here</a>.</p>
<hr>

<address>Apache/2.2.15 (CentOS) Server at www.boost.org Port 80</address> 

Solution:-

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>

using namespace boost::asio;
using namespace boost::asio::ip;
using namespace std::chrono_literals;

constexpr size_t BUFFER_SIZE = 1024;

std::string read_request(tcp::socket& socket) {
    boost::asio::streambuf buf;
    boost::asio::read_until(socket, buf, "\r\n\r\n");
    std::string request(boost::asio::buffer_cast<const char*>(buf.data()), buf.size());
    return request;
}

void parse_request(const std::string& request, std::string& host, std::string& path) {
    // Extract host and path from the request
    // This is a simplistic implementation, you may need to enhance it
    size_t host_start = request.find("Host: ") + 6;
    size_t host_end = request.find("\r\n", host_start);
    host = request.substr(host_start, host_end - host_start);

    size_t path_start = request.find(" ", request.find("GET")) + 1;
    size_t path_end = request.find(" ", path_start);
    path = request.substr(path_start, path_end - path_start);
}
void handle_client(tcp::socket socket, std::string dest_host, unsigned short dest_port, io_context& io_ctx, yield_context yield) {
    try {
        std::string request = read_request(socket);
        std::cout << "Received request: " << request << std::endl;
        std::string host, path;
        parse_request(request, host, path);

        tcp::resolver resolver(io_ctx);
        tcp::resolver::query query(dest_host, std::to_string(dest_port));
        auto endpoint_iterator = resolver.async_resolve(query, yield);

        tcp::socket dest_socket(io_ctx);
        boost::asio::async_connect(dest_socket, endpoint_iterator, yield);

        boost::asio::async_write(dest_socket, buffer(request), yield);

        char data[BUFFER_SIZE];
        size_t length = dest_socket.async_read_some(buffer(data), yield);
        std::string response(data, length);

        std::cout << "Received response: " << response << std::endl;

        boost::asio::async_write(socket, buffer(response), yield);

    } catch (const std::exception& e) {
        std::cerr << "Exception in handle_client: " << e.what() << std::endl;
    }
}
int main(int argc, char* argv[]) {
    try {
        if (argc != 4) {
            std::cerr << "Usage: ./a.out <local_port> <destination_host> <destination_port>" << std::endl;
            return 1;
        }

        unsigned short local_port = std::atoi(argv[1]);
        std::string dest_host = argv[2];
        unsigned short dest_port = std::atoi(argv[3]);

        io_context io_ctx;

        tcp::acceptor acceptor(io_ctx, tcp::endpoint(tcp::v4(), local_port));

        for (;;) {
            tcp::socket socket(io_ctx);
            acceptor.async_accept(socket, [&](const boost::system::error_code& error) {
                if (!error) {
                    boost::asio::spawn(io_ctx, [&](yield_context yield) {
                        handle_client(std::move(socket), dest_host, dest_port, io_ctx, yield);
                    });
                }
            });
            io_ctx.run();
        }

    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

Comments

Popular posts from this blog

GlobalLogic Interview

Motorola(Dynamic Technologies) Kolkata

Euronet interview questions