1

I am building a web-server. I am trying to build a function handler that parses the index.html file in the root directory. It works but when I go to the website on my localhost 127.0.0.1:8080 I get page not found. I programmed it to say that when the ifstream has falied. I'm positive it failed because it cannot find the root directory.

WebServer.h

// Handler for when a message is received from the client
void WebServer::onHTMLReceived(int clientSocket, const char* msg, int length)
{
    // Parse out the client's request string e.g. GET /index. HTTP/1.1
    std::istringstream iss(msg);
    std::vector<std::string> parsed((std::istream_iterator<std::string>(iss)), std::istream_iterator<std::string>());

    // Some defaults for output to the client (404 file not found 'page')
    std::string content = "<h1>404 Not Found</h1>";
    std::string htmlFile = "/index.html";
    int errorCode = 404;

    // If the GET request is valid, try and get the name
    if (parsed.size() >= 3 && parsed[0] == "GET")
    {
        htmlFile = parsed[1];

        // If the file is just a slash, use index.html. This should really
        // be if it _ends_ in a slash. I'll leave that for you :)
        if (htmlFile == "/")
        {
            htmlFile = "/index.html";
        }

    }

    // Open the document in the local file system 
    // The problem is here, ".\\wwwroot" isn't working 

    std::ifstream f(".\\wwwroot" + htmlFile);

    // Check if it opened and if it did, grab the entire contents
    if (f.good())
    {
        std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
        content = str;
        errorCode = 200;
    }

    f.close();

    // Write the document back to the client
    std::ostringstream oss;
    oss << "HTTP/1.1 " << errorCode << " OK\r\n";
    oss << "Cache-Control: no-cache, private\r\n";
    oss << "Content-Type: text/html\r\n";
    oss << "Content-Length: " << content.size() << "\r\n";
    oss << "\r\n";
    oss << content;

    std::string output = oss.str();
    int size = output.size() + 1;

    send(clientSocket, output.c_str(), size, 0);
    std::cout << "" << std::endl;
}

I thought .\\wwwroot would take me to the root directory but it didn't. I don't think that works for windows. When I go to 127.0.0.1 in the web-browser I get this output in the terminal:

output:

CLIENT> GET /favicon.ico HTTP/1.1
Host: localhost:8080
Connection: keep-alive
sec-ch-ua-platform: "Windows"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"
sec-ch-ua-mobile: ?0
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9

Does anyone have any suggestions?

I trid to use:

 namespace fs = std::filesystem;
 fs::path path(fs::current_path());
 std::u8string path_string{path.u8string()};

But the compiler says namespace "std" has no member "u8string". I'm not sure if that is because of the version I am using or not.

4
  • Is wwwroot in the same directory as you ran your program from? Have you tried using an absolute path or using a debugger? Commented Dec 21, 2024 at 17:58
  • 2
    The "root directory" you specify is relative to the server's current working directory, which is probably not what you're assuming it to be. Commented Dec 21, 2024 at 17:59
  • On a side note: int size = output.size() + 1; is wrong. You are including the output's null terminator in the data that you send() to the client, but HTTP doesn't send null terminators. Commented Dec 21, 2024 at 18:05
  • 2
    Be careful of requests for files with .. in the path, as they can be used to steal any file in your file system if you don't take care to handle them. Commented Dec 21, 2024 at 18:39

1 Answer 1

2

I thought .\\wwwroot would take me to the root directory but it didn't.

No, it doesn't. In this case, the . represents the Current Working Directory of the calling process. You can use std::filesystem::current_path() to determine what that directory path actually is at runtime.

But, you should not rely on this behavior! The CWD is dynamic. By default, it is the folder that the EXE is located in, but it can also be set explicitly when the process is started, such as when launching the EXE from a shortcut. And more importantly, the CWD may change value while the process is running, so it may not always be pointing where you are expecting.

You should instead explicitly specify your desired root folder in your app's configuration, eg:

fs::path rootPath = "C:\\wwwroot\\"; // store this value in your app's config somewhere...
...
if (htmlFile.empty() || (htmlFile.front() != '/')) {
    // send back an error...
    return;
}

htmlFile.erase(0, 1);
if (htmlFile.empty())
    htmlFile = "index.html";

fs::path filePath = rootPath / htmlFile;
if (!fs::exists(filePath)) {
    // send back an error...
    return;
}

std::ifstream f(filePath);
...
Sign up to request clarification or add additional context in comments.

6 Comments

I presume fs = std::filesystem in your code (I am not a big fan of namespace aliases specially not in examples)
@PepijnKramer that is in the OP's code already (namespace fs = std::filesystem;)
Me bad I missed that, consider my remark for OP then ;)
Ok so how do I set the cwd if I am using VS code ?
std::filesystem::current_path() can both query and set the CWD. But, you really should NOT be relying on the CWD at all. What's wrong with the approach I gave you? It is much safer and more reliable.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.