Git Product home page Git Product logo

sorin373 / http-server Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 521 KB

In order to complete the CS50 Harvard course I created for my final project an HTTP server, tailored for local network file transfers.

Home Page: https://www.youtube.com/watch?v=OeKAQjgdvNc&ab_channel=SorinTudose

License: MIT License

CMake 2.57% C++ 82.16% HTML 7.19% CSS 4.27% JavaScript 3.30% Shell 0.51%
tcp-client tcp-server tcp-socket unix-network-programing http-server cs50-final-project cs50x cs50

http-server's Introduction

Project Logo or Banner

In order to complete the CS50 course I was required to build a personal computer science project. As a result, I decided to implement a simple HTTP server, which enables users to upload and download files within their local network.

License: MIT

Table of Contents

Installation

Unix (Debian)

  1. Download the Debian package (.deb file) from the GitHub Releases page.
  2. Install the package using the software installer
  3. By default, the installation path is the /usr/httpServer directory. To run the application, open your terminal here and execute:
./httpServer [port] [-debug]

Unix (RPM-based)

  1. Download the RPM package (.rpm file) from the GitHub Releases page.
  2. Install the package using the rpm command:
sudo rpm -i httpServer-1.0.0-Linux.rpm
  1. By default, the installation path is the /usr/httpServer directory. To run the application, open your terminal here and execute:
./httpServer [port] [-debug]

Building the executable binaries

In order to build the application CMake and MySql-Connector-CPP need to be installed.

sudo apt-get update && sudo apt-get upgrade
  1. Install CMake
sudo snap install cmake --classic
  1. MySql-Connector-CPP
sudo apt-get install libmysqlcppconn-dev
  • After that, download the zip file containing the latest project files from the GitHub repository, open the folder in Visual Studio Code and use the CMake commands to build the application (or install the CMake and CMake Tools extensions).

Server functionalities

  • This HTTP server has the ability to distinguish between GET and POST requests. It automatically sends the request to the correct function where it is processed.
  • The server runs on a thread, detached from the main function, together with another thread that listens for console input.
  • When the binding process is performed the user's internet address is read automatically by running ifconfig in the background. The address, together with the port number will be displayed in the console if the server starts successfully, for the user to paste them in the browser.
  • The default port number, the server listens to is 8080.
  • The server supports multiple connections at the same time, the connected sockets being stored in a vector.
  • When running the application you can specify any valid port number if you wish to override the default one. Moreover, when the application is also ran using the -debug flag, the server admin (this means that -debug only affects the console output on the machine, the server was started on) can vizualize the HTTP requests and serveral other error messages. This, however, does not mean that when in default mode there are no errors being shown on the screen.
  • Last but not least, the server displays any SQL errors produced by the program.

Note For more information about the sever features there is more documentation in the server header file.

Features

Note: All functions that are related to the routing process are also documented in the interface header.

Upload files

  • The application supports file uploading which enables users to share files between machines on a local network. Using an HTML form element, an HTTP POST request is sent to the server containg the file data together with the request metadata. This is later stored in a temp.bin file for later formatting. After receiving all the HTTP request contents, the file data is extracted and stored locally in a file, ensuring it has the appropriate name and extension. The file formatting consists in:
  1. Retrieving the file name together with the file extension

    if (findString(buffer, "filename="))
    {
        fileName.clear();
    
        const std::string t_buffer = std::string(buffer);
    
        // define the pattern
        std::regex fileNameRegex(R"(filename=\"([^\"]+)\")");
        std::smatch match;
    
        // searches in the string 't_buffer' for matches
        if (std::regex_search(t_buffer, match, fileNameRegex)) fileName = match.str(1);
    
        // Variable used to generate unique name for files which do not have a valid file name
        fileCount++;
    
        if (fileName.length() > 40)
        {
            std::string fileExtension;
            std::string newName;
    
            newName = "Upload" + std::to_string(fileCount);
    
            size_t pos = fileName.find(".");
    
            if (pos != std::string::npos)
            {
                fileExtension = fileName.substr(pos);
                newName = newName + fileExtension;
            }
    
            fileName = newName;
        }
        else
        {
            // Replacing spaces with underlines
            for (unsigned int i = 0, n = fileName.length(); i < n; i++) if (fileName[i] == ' ') fileName[i] = '_';
        }
    
        // Adds the new file in the queue to be inserted in the database
        addFileInQueue(fileName);
    }
  2. Extracting the file content located between the two boundaries (------WebKitFormBoundary)

    std::string line;
    bool foundBoundary = false;
    
    while (std::getline(file, line))
    {
        // Check for boundary
        if (!foundBoundary && line.find("------WebKitFormBoundary") != std::string::npos)
        {
            foundBoundary = true;
            continue;
        }
    
        if (foundBoundary && line.find("Content-Type:") != std::string::npos)
        {
            std::getline(file, line); // Skip line
    
            while (std::getline(file, line))
            {
                // Read until the second boundary is found
                if (line.find("------WebKitFormBoundary") != std::string::npos)
                    break;
    
                outFile << line << std::endl;
            }
    
            break;
        }
    }
    
    file.close();
    outFile.close();
    
    // Remove 'temp.bin'
    if (remove(BINARY_FILE_TEMP_PATH) != 0) std::cerr << "Failed to remove: " << BINARY_FILE_TEMP_PATH << "\n";

Delete files

  • The user has the ability to delete the files added to the server. This is done by clicking a button, which in return sends an HTTP POST request to the server. This request contains the file ID which the application uses to delete the file from the database.

  • Moreover, after the file is removed index.html is rebuilt and the file table is fetched.

    // Fetch the file name using the file ID for later use
    std::string query = "SELECT name FROM file WHERE file_id=(?)";
    sql::ResultSet *res = nullptr;
    
    sql::PreparedStatement *prepStmt = server->getSQLdatabase()->getCon()->prepareStatement(query);
    
    prepStmt->setInt(1, fileID);
    res = prepStmt->executeQuery();
    
    while (res->next()) std::string fileName = res->getString("name");
    
    delete res;
    delete prepStmt;
    
    // Delete the file from the database using the file ID
    std::string deleteQuery = "DELETE FROM file WHERE file_id=(?)";
    
    prepStmt = server->getSQLdatabase()->getCon()->prepareStatement(deleteQuery);
    
    prepStmt->setInt(1, fileID);
    prepStmt->executeUpdate();
    
    delete prepStmt;
    
    SQLfetchFileTable();
    buildIndexHTML();
    
    // Using the file name remove the file from local storage
    std::string fileToDelete = std::string(LOCAL_STORAGE_PATH) + fileName;
    remove(fileToDelete.c_str());

Create account

  • In addition, users are also able to create a new account on the server. The createAccount.html page is prebuilt and it is fetched using a HTTP GET request sent by the browser. After correctly filling in the form, an HTTP POST request is sent containg all the reuquired data for an account to be opened: Username, Password, and the Password Confirmation. Once these are validated, the information about the user account is insterted into the database and the user is redirected back to the login.

  • It is important to know that the user accounts can not yet be deleted once they are created and that the usernames are unique throughout the database.

    // Check if the username already exists in the database
    if (findUsername(username))
    {
        if (send(acceptedSocketFileDescriptor, unauthorized, strlen(unauthorized), 0) == -1)
        {
            std::cerr << std::setw(5) << " "
                      << "--> Error: Failed to send response.\n";
            return EXIT_FAILURE;
        }
    
        return EXIT_SUCCESS;    
    }
    
    // Check if the password is the same as the confirmation
    if (strcmp(password, confirmation) != 0)
    {
        if (send(acceptedSocketFileDescriptor, unauthorized, strlen(unauthorized), 0) == -1)
        {
            std::cerr << std::setw(5) << " "
                      << "--> Error: Failed to send response.\n";
            return EXIT_FAILURE;
        }
    
        return EXIT_SUCCESS;
    }
    
    // Prepare query to insert the new account information
    std::string query = "INSERT INTO User VALUES (?, ?, ?)";
    sql::PreparedStatement *prepStmt = server->getSQLdatabase()->getCon()->prepareStatement(query);
    
    prepStmt->setInt(1, uc.size());
    prepStmt->setString(3, std::string(password));
    prepStmt->setString(2, std::string(username));
    
    // Execute the query
    prepStmt->executeUpdate();
    
    delete prepStmt;
    
    // Fetch the User table containg the updated data
    SQLfetchUserTable();

Change password

  • Finally, users can change their password. This can be done ONLY IF the current password has not been forgotten.

  • On the login.html page there is a button when clicked sends an HTTP GET request to the server in order to fetch the changePassword.html file. After reaching this web page the users needs to fill in another form consiting of: username, Old Password, New Password and the New Password Confirmation. If these fields contain valid pieces of data, the new user account's password will be updated in the database.

    // Check if old credentials are valid
    if (!validateCredentials(username, oldPassword))
    {
        if (send(acceptedSocketFileDescriptor, unauthorized, strlen(unauthorized), 0) == -1)
        {
            std::cerr << std::setw(5) << " "
                      << "--> Error: Failed to send response.\n";
            return EXIT_FAILURE;
        }
    
        return EXIT_SUCCESS;
    }
    
    // Check if the new password is the same as the confirmation
    if (strcmp(newPassword, confirmation) != 0)
    {
        if (send(acceptedSocketFileDescriptor, unauthorized, strlen(unauthorized), 0) == -1)
        {
            std::cerr << std::setw(5) << " "
                      << "--> Error: Failed to send response.\n";
            return EXIT_FAILURE;
        }
    
        return EXIT_SUCCESS;
    }
    
    // Prepare query to update the User
    std::string query = "UPDATE User SET password=(?) WHERE username=(?)";
    sql::PreparedStatement *prepStmt = server->getSQLdatabase()->getCon()->prepareStatement(query);
    
    prepStmt->setString(1, std::string(newPassword));
    prepStmt->setString(2, std::string(username));
    
    // Execute the query
    prepStmt->executeUpdate();
    
    delete prepStmt;
    
    // Fetch the User table containg the updated data
    SQLfetchUserTable();

Note: If the form on each page is submitted correctly the user will be redirected to the login.html page. However, if the data is incorrect the user should be redirected to apology.html.

Note: All input fields are checked for correct length using C++ logic in order to avoid any buffer overflows.


MySql-Database

img

  • The httpServer database contains two tables: the file table and the user table.

  • The relationship between the tables is One-to-Many.

  • My application communicates with the MySql database using the MySql-Connector-CPP library.

  • Before running the application a local MySql database, which will house the necessary data, needs to be created (either commands or the MySql Workbench can be used for this task).

  • I provide the table formats and the self-contained file in the demo folder for a quicker setup.


Note: I do not provide any sample data. The database is meant to be empty when first used.
Users can create accounts, add files and delete files when using the application.

Note: It is highly important that the table names are kept, as they are hard coded in the program. Having said that the database name is up to you as it is required to enter it when the application is ran.


Bibliography

Contact

I hope this project was beneficial and helpful. If you want to get in touch with me, you can do so through my personal email:

[email protected]

Wishing you a delightful and productive day!

http-server's People

Contributors

sorin373 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.