Fillyjonk
Fillyjonk

Reputation: 178

Serving static files in uWebSockets HTTP server (C++)

I am setting up an HTTP server in C++ using the uWebSockets library and I would like to add a middleware to serve static files, similar to what app.use(express.static(path.join(__dirname, 'public'))); does in Express.js.

The static files reside in the public folder. The middleware should make the server load files under the path http://localhost:8087/css/bootstrap.min.css, not http://localhost:8087/public/css/bootstrap.min.css, thus re-routing root to public.

How could one do this in C++ using the uWebSockets library? I already inspected the uWS::App struct, however I find there nothing related to the path or to serving static files.

Here is an example an HTTP server:

#include <uWebSockets/App.h>
#include <rapidjson/rapidjson.h>

#include "util/AsyncFileReader.h"
#include "util/AsyncFileStreamer.h"

#include <iostream>
#include <string>



void get_home(uWS::HttpResponse<false> *res, uWS::HttpRequest *req) {
  res->writeHeader("Content-Type", "text/html; charset=utf8");
  // res->writeStatus(uWS::HTTP_200_OK);
  // res->end("Hello! This is <b>Sergei's C++ web server</b>.");
  AsyncFileReader page_contents("./public/home.html");
  res->end(page_contents.peek(0));
}

int main() {
  int port{8087};

  // HTTP
  uWS::App app = uWS::App();
  app.get("/", get_home);


  app.listen(port, [&port](auto *token) {
    if (token) {
      std::cout << "Listening on port " << port << std::endl;
    }
  })
  .run();

  return 0;
}

Upvotes: 0

Views: 1484

Answers (2)

amit.user105387
amit.user105387

Reputation: 76

There is an example with this exactly

I eventually ended up adding a directory watch and updating the html files if saved (a few changes in codebase) but i guess thats a different thing

#include "helpers/AsyncFileReader.h"
#include "helpers/AsyncFileStreamer.h"
#include "helpers/Middleware.h"

AsyncFileStreamer asyncFileStreamer("htmls"); // htmls is a relative folder path to static files

app.get("/*", gethome); // note the *
void get_home(auto *res, auto *req) {
//void get_home(uWS::HttpResponse<false> *res, uWS::HttpRequest *req) {
  
  serveFile(res, req); // essentially res->writeStatus(uWS::HTTP_200_OK);
  asyncFileStreamer.streamFile(res, req->getUrl());
}

Please note serveFile() function also needs to take care of different Content-Type header setting for images

example mentioned: https://github.com/uNetworking/uWebSockets/blob/master/examples/HttpServer.cpp

Upvotes: 1

Fillyjonk
Fillyjonk

Reputation: 178

To expand the answer of @amit.user105387, here is how a working solution could look like:

#include <uWebSockets/App.h>
#include <rapidjson/rapidjson.h>

#include "util/AsyncFileReader.h"
#include "util/AsyncFileStreamer.h"
#include "util/Middleware.h"

#include <iostream>
#include <string>

void get_home(uWS::HttpResponse<false> *res, uWS::HttpRequest *req) {
  // res->writeHeader("Alt-Svc", "h2=\"localhost:8087\"public\"");
  res->writeHeader("Content-Type", "text/html; charset=utf8");
  AsyncFileReader page_contents("./public/html/home.html");
  res->end(page_contents.peek(0));
}

void PrintCachedFiles(std::map<std::string_view, AsyncFileReader *> dict) {
  for (auto it = dict.begin(); it != dict.end(); it++) {
    std::cout << it->first << std::endl;
  }
}

int main() {
  const int port{8087};
  const std::string public_folder{"./public"};
  AsyncFileStreamer public_files("./public");

  // HTTP
  uWS::App app = uWS::App();

  app.get("/", [&public_files](auto res, auto req){
    std::cout << "root folder = "<< public_files.root << std::endl;
    PrintCachedFiles(public_files.asyncFileReaders);
    res->writeHeader("Content-Type", "text/html; charset=utf8");
    public_files.streamFile(res, "/html/home.html");
    res->writeStatus(uWS::HTTP_200_OK)->end();
  });

  app.get("/css/:path", [&public_files](auto res, auto req){
    public_files.streamFile(res, req->getUrl());
    res->writeStatus(uWS::HTTP_200_OK)->end();
  });

  app.get("/js/:path", [&public_files](auto res, auto req){
    public_files.streamFile(res, req->getUrl());
    res->writeStatus(uWS::HTTP_200_OK)->end();
  });

  app.get("/img/:path/*", [&public_files](auto res, auto req){
    serveFile(res, req);
    public_files.streamFile(res, req->getUrl());
    res->writeStatus(uWS::HTTP_200_OK)->end();
  });

  app.get("/*", [&public_files](auto res, auto req){    
    res->writeStatus(uWS::HTTP_200_OK)->end("404: Page not found.");
  });


  app.listen(port, [&port](auto *token) {
    if (token) {
      std::cout << "Listening on port " << port << std::endl;
    }
  })
  .run();

  return 0;
}

Upvotes: 0

Related Questions