🎯 Exemples recommandés
Balanced sample collections from various categories for you to explore
Exemples de Fonctionnalités Web Windows C++
Exemples de développement de serveur web Windows C++ incluant le routage URL, le middleware et le service de fichiers statiques
💻 Routage URL cpp
🟡 intermediate
⭐⭐⭐⭐
Implémenter l'analyse et la correspondance des routes URL avec paramètres de chemin et chaînes de requête
⏱️ 35 min
🏷️ cpp, web, routing, http, windows
Prerequisites:
Intermediate C++, HTTP protocol basics
// Windows C++ URL Routing Examples
// Using Windows HTTP Server API
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <regex>
#include <windows.h>
#include <http.h>
#pragma comment(lib, "httpapi.lib")
// 1. Basic Route Structure
struct Route {
std::string method;
std::string path;
std::function<void(const std::map<std::string, std::string>&)> handler;
Route(const std::string& m, const std::string& p,
std::function<void(const std::map<std::string, std::string>&)> h)
: method(m), path(p), handler(h) {}
};
// 2. URL Parser
class URLParser {
public:
static std::string getPath(const std::string& url) {
size_t queryPos = url.find('?');
if (queryPos == std::string::npos) {
return url;
}
return url.substr(0, queryPos);
}
static std::map<std::string, std::string> getQueryParams(const std::string& url) {
std::map<std::string, std::string> params;
size_t queryPos = url.find('?');
if (queryPos == std::string::npos) {
return params;
}
std::string queryString = url.substr(queryPos + 1);
size_t start = 0;
while (start < queryString.length()) {
size_t ampPos = queryString.find('&', start);
std::string pair;
if (ampPos == std::string::npos) {
pair = queryString.substr(start);
break;
} else {
pair = queryString.substr(start, ampPos - start);
start = ampPos + 1;
}
size_t eqPos = pair.find('=');
if (eqPos != std::string::npos) {
std::string key = pair.substr(0, eqPos);
std::string value = pair.substr(eqPos + 1);
params[key] = value;
}
}
return params;
}
static std::string decodeURL(const std::string& encoded) {
std::string decoded;
size_t i = 0;
while (i < encoded.length()) {
if (encoded[i] == '%' && i + 2 < encoded.length()) {
std::string hexStr = encoded.substr(i + 1, 2);
int charCode = std::stoi(hexStr, nullptr, 16);
decoded += static_cast<char>(charCode);
i += 3;
} else if (encoded[i] == '+') {
decoded += ' ';
i++;
} else {
decoded += encoded[i];
i++;
}
}
return decoded;
}
};
// 3. Simple Router
class Router {
private:
std::vector<Route> routes;
public:
void addRoute(const std::string& method, const std::string& path,
std::function<void(const std::map<std::string, std::string>&)> handler) {
routes.emplace_back(method, path, handler);
}
void get(const std::string& path,
std::function<void(const std::map<std::string, std::string>&)> handler) {
addRoute("GET", path, handler);
}
void post(const std::string& path,
std::function<void(const std::map<std::string, std::string>&)> handler) {
addRoute("POST", path, handler);
}
void put(const std::string& path,
std::function<void(const std::map<std::string, std::string>&)> handler) {
addRoute("PUT", path, handler);
}
void del(const std::string& path,
std::function<void(const std::map<std::string, std::string>&)> handler) {
addRoute("DELETE", path, handler);
}
bool handleRequest(const std::string& method, const std::string& url) {
std::string path = URLParser::getPath(url);
std::map<std::string, std::string> queryParams = URLParser::getQueryParams(url);
for (const auto& route : routes) {
if (route.method == method && route.path == path) {
route.handler(queryParams);
return true;
}
}
return false;
}
};
// 4. Parameterized Route Matcher
class ParameterizedRouter {
private:
struct RoutePattern {
std::regex pattern;
std::vector<std::string> paramNames;
std::function<void(const std::map<std::string, std::string>&)> handler;
};
std::vector<RoutePattern> routes;
public:
void addRoute(const std::string& method, const std::string& pathPattern,
std::function<void(const std::map<std::string, std::string>&)> handler) {
std::regex paramRegex(R"({(w+)})");
std::string regexPattern = pathPattern;
std::vector<std::string> paramNames;
// Replace {param} with regex groups
std::sregex_iterator it(pathPattern.begin(), pathPattern.end(), paramRegex);
std::sregex_iterator end;
for (; it != end; ++it) {
paramNames.push_back((*it)[1].str());
}
regexPattern = std::regex_replace(regexPattern, paramRegex, R"(([^/]+))");
regexPattern = "^" + regexPattern + "$";
RoutePattern route;
route.pattern = std::regex(regexPattern);
route.paramNames = paramNames;
route.handler = handler;
routes.push_back(route);
}
bool handleRequest(const std::string& method, const std::string& url,
std::map<std::string, std::string>& pathParams) {
std::string path = URLParser::getPath(url);
for (const auto& route : routes) {
std::smatch matches;
if (std::regex_match(path, matches, route.pattern)) {
for (size_t i = 0; i < route.paramNames.size() && i + 1 < matches.size(); ++i) {
pathParams[route.paramNames[i]] = matches[i + 1].str();
}
route.handler(pathParams);
return true;
}
}
return false;
}
};
// 5. RESTful Router
class RESTfulRouter {
private:
struct ResourceHandler {
std::function<void()> getList;
std::function<void(const std::string&)> getOne;
std::function<void()> create;
std::function<void(const std::string&)> update;
std::function<void(const std::string&)> remove;
};
std::map<std::string, ResourceHandler> resources;
public:
void resource(const std::string& path, const ResourceHandler& handler) {
resources[path] = handler;
}
bool handleRequest(const std::string& method, const std::string& url) {
for (const auto& entry : resources) {
const std::string& basePath = entry.first;
const ResourceHandler& handler = entry.second;
// List and Create
if (url == basePath || url == basePath + "/") {
if (method == "GET" && handler.getList) {
handler.getList();
return true;
} else if (method == "POST" && handler.create) {
handler.create();
return true;
}
}
// Get, Update, Delete by ID
std::string withId = basePath + "/([^/]+)";
std::regex idRegex("^" + withId + "$");
std::smatch matches;
if (std::regex_match(url, matches, idRegex) && matches.size() > 1) {
std::string id = matches[1].str();
if (method == "GET" && handler.getOne) {
handler.getOne(id);
return true;
} else if (method == "PUT" && handler.update) {
handler.update(id);
return true;
} else if (method == "DELETE" && handler.remove) {
handler.remove(id);
return true;
}
}
}
return false;
}
};
// 6. Route Group with Prefix
class RouteGroup {
private:
std::string prefix;
Router& router;
public:
RouteGroup(const std::string& p, Router& r) : prefix(p), router(r) {}
void get(const std::string& path,
std::function<void(const std::map<std::string, std::string>&)> handler) {
router.get(prefix + path, handler);
}
void post(const std::string& path,
std::function<void(const std::map<std::string, std::string>&)> handler) {
router.post(prefix + path, handler);
}
void put(const std::string& path,
std::function<void(const std::map<std::string, std::string>&)> handler) {
router.put(prefix + path, handler);
}
void del(const std::string& path,
std::function<void(const std::map<std::string, std::string>&)> handler) {
router.del(prefix + path, handler);
}
};
// 7. Middleware Support
class MiddlewareRouter {
private:
struct RouteWithMiddleware {
std::string method;
std::string path;
std::vector<std::function<void()>> middlewares;
std::function<void()> handler;
};
std::vector<RouteWithMiddleware> routes;
public:
void addRoute(const std::string& method, const std::string& path,
std::vector<std::function<void()>> middlewares,
std::function<void()> handler) {
RouteWithMiddleware route;
route.method = method;
route.path = path;
route.middlewares = middlewares;
route.handler = handler;
routes.push_back(route);
}
bool handleRequest(const std::string& method, const std::string& path) {
for (const auto& route : routes) {
if (route.method == method && route.path == path) {
// Execute middlewares
for (const auto& middleware : route.middlewares) {
middleware();
}
// Execute handler
route.handler();
return true;
}
}
return false;
}
};
// 8. HTTP Method Router
class HTTPMethodRouter {
private:
std::map<std::string, std::map<std::string, std::function<void()>>> routes;
std::function<void()> notFoundHandler;
public:
void setNotFoundHandler(std::function<void()> handler) {
notFoundHandler = handler;
}
void addRoute(const std::string& method, const std::string& path,
std::function<void()> handler) {
routes[method][path] = handler;
}
bool handleRequest(const std::string& method, const std::string& path) {
auto methodIt = routes.find(method);
if (methodIt != routes.end()) {
auto pathIt = methodIt->second.find(path);
if (pathIt != methodIt->second.end()) {
pathIt->second();
return true;
}
}
if (notFoundHandler) {
notFoundHandler();
}
return false;
}
// Support for HEAD requests (auto-generated from GET)
bool supportsHEAD(const std::string& path) {
auto getIt = routes.find("GET");
if (getIt != routes.end()) {
return getIt->second.find(path) != getIt->second.end();
}
return false;
}
// Support for OPTIONS requests
std::vector<std::string> allowedMethods(const std::string& path) {
std::vector<std::string> methods;
for (const auto& entry : routes) {
if (entry.second.find(path) != entry.second.end()) {
methods.push_back(entry.first);
}
}
return methods;
}
};
// Main demonstration
int main() {
std::cout << "=== Windows C++ URL Routing Examples ===" << std::endl;
// 1. Basic Router
std::cout << "\n--- 1. Basic Router ---" << std::endl;
Router router;
router.get("/", [](const std::map<std::string, std::string>& params) {
std::cout << "GET / - Home Page" << std::endl;
});
router.get("/about", [](const std::map<std::string, std::string>& params) {
std::cout << "GET /about - About Page" << std::endl;
});
router.post("/submit", [](const std::map<std::string, std::string>& params) {
std::cout << "POST /submit - Form Submission" << std::endl;
});
router.handleRequest("GET", "/");
router.handleRequest("GET", "/about");
router.handleRequest("POST", "/submit");
// 2. Query Parameters
std::cout << "\n--- 2. Query Parameters ---" << std::endl;
std::string testUrl = "/search?q=routing&page=2&limit=10";
auto queryParams = URLParser::getQueryParams(testUrl);
std::cout << "Query params for: " << testUrl << std::endl;
for (const auto& param : queryParams) {
std::cout << " " << param.first << " = " << param.second << std::endl;
}
// 3. Parameterized Routes
std::cout << "\n--- 3. Parameterized Routes ---" << std::endl;
ParameterizedRouter paramRouter;
paramRouter.addRoute("GET", "/users/{id}",
[](const std::map<std::string, std::string>& params) {
auto it = params.find("id");
if (it != params.end()) {
std::cout << "GET /users/{id} - User ID: " << it->second << std::endl;
}
});
paramRouter.addRoute("GET", "/posts/{postId}/comments/{commentId}",
[](const std::map<std::string, std::string>& params) {
std::cout << "GET /posts/{postId}/comments/{commentId}" << std::endl;
for (const auto& p : params) {
std::cout << " " << p.first << " = " << p.second << std::endl;
}
});
std::map<std::string, std::string> pathParams;
paramRouter.handleRequest("GET", "/users/123", pathParams);
paramRouter.handleRequest("GET", "/posts/45/comments/789", pathParams);
// 4. RESTful Router
std::cout << "\n--- 4. RESTful Router ---" << std::endl;
RESTfulRouter restRouter;
RESTfulRouter::ResourceHandler userHandler;
userHandler.getList = []() { std::cout << "GET /users - List all users" << std::endl; };
userHandler.getOne = [](const std::string& id) { std::cout << "GET /users/" << id << " - Get user" << std::endl; };
userHandler.create = []() { std::cout << "POST /users - Create user" << std::endl; };
userHandler.update = [](const std::string& id) { std::cout << "PUT /users/" << id << " - Update user" << std::endl; };
userHandler.remove = [](const std::string& id) { std::cout << "DELETE /users/" << id << " - Delete user" << std::endl; };
restRouter.resource("/users", userHandler);
restRouter.handleRequest("GET", "/users");
restRouter.handleRequest("GET", "/users/42");
restRouter.handleRequest("POST", "/users");
restRouter.handleRequest("PUT", "/users/42");
restRouter.handleRequest("DELETE", "/users/42");
// 5. Route Groups
std::cout << "\n--- 5. Route Groups ---" << std::endl;
Router apiRouter;
RouteGroup apiGroup("/api/v1", apiRouter);
apiGroup.get("/users", [](const auto& p) { std::cout << "GET /api/v1/users" << std::endl; });
apiGroup.get("/products", [](const auto& p) { std::cout << "GET /api/v1/products" << std::endl; });
apiGroup.post("/orders", [](const auto& p) { std::cout << "POST /api/v1/orders" << std::endl; });
apiRouter.handleRequest("GET", "/api/v1/users");
apiRouter.handleRequest("GET", "/api/v1/products");
apiRouter.handleRequest("POST", "/api/v1/orders");
// 6. HTTP Method Router
std::cout << "\n--- 6. HTTP Method Router ---" << std::endl;
HTTPMethodRouter methodRouter;
methodRouter.addRoute("GET", "/resource", []() {
std::cout << "GET /resource" << std::endl;
});
methodRouter.addRoute("POST", "/resource", []() {
std::cout << "POST /resource" << std::endl;
});
methodRouter.setNotFoundHandler([]() {
std::cout << "404 - Not Found" << std::endl;
});
methodRouter.handleRequest("GET", "/resource");
methodRouter.handleRequest("POST", "/resource");
methodRouter.handleRequest("DELETE", "/resource");
// Check allowed methods
auto methods = methodRouter.allowedMethods("/resource");
std::cout << "Allowed methods for /resource: ";
for (const auto& m : methods) {
std::cout << m << " ";
}
std::cout << std::endl;
std::cout << "\n=== All URL Routing Examples Completed ===" << std::endl;
return 0;
}
💻 Middleware HTTP cpp
🟡 intermediate
⭐⭐⭐⭐
Implémenter un pipeline de middleware de traitement des requêtes pour la journalisation, l'authentification et la gestion CORS
⏱️ 40 min
🏷️ cpp, web, middleware, http, windows
Prerequisites:
Intermediate C++, HTTP protocol, Functional programming
// Windows C++ HTTP Middleware Examples
// Implementing middleware pipeline for web server
#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <map>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <sstream>
// 1. HTTP Request/Response Structures
struct HttpRequest {
std::string method;
std::string path;
std::map<std::string, std::string> headers;
std::string body;
};
struct HttpResponse {
int statusCode;
std::map<std::string, std::string> headers;
std::string body;
HttpResponse() : statusCode(200) {}
};
// 2. Middleware Function Type
using MiddlewareFunc = std::function<bool(HttpRequest&, HttpResponse&)>;
using HandlerFunc = std::function<void(HttpRequest&, HttpResponse&)>;
// 3. Logging Middleware
class LoggingMiddleware {
private:
std::vector<std::string> logs;
public:
bool operator()(HttpRequest& req, HttpResponse& res) {
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S");
std::string logEntry = "[" + ss.str() + "] " + req.method + " " + req.path;
logs.push_back(logEntry);
std::cout << "[LOG] " << logEntry << std::endl;
return true; // Continue to next middleware
}
void displayLogs() const {
std::cout << "\n--- Request Logs ---" << std::endl;
for (const auto& log : logs) {
std::cout << log << std::endl;
}
}
};
// 4. Authentication Middleware
class AuthMiddleware {
private:
std::map<std::string, std::string> validTokens; // token -> username
public:
AuthMiddleware() {
// Pre-populate with some test tokens
validTokens["abc123"] = "alice";
validTokens["xyz789"] = "bob";
}
bool operator()(HttpRequest& req, HttpResponse& res) {
// Check for Authorization header
auto it = req.headers.find("Authorization");
if (it == req.headers.end()) {
res.statusCode = 401;
res.body = R"({"error": "Missing authorization header"})";
res.headers["Content-Type"] = "application/json";
return false; // Stop pipeline
}
std::string token = it->second;
// Remove "Bearer " prefix if present
if (token.find("Bearer ") == 0) {
token = token.substr(7);
}
// Validate token
auto tokenIt = validTokens.find(token);
if (tokenIt == validTokens.end()) {
res.statusCode = 401;
res.body = R"({"error": "Invalid token"})";
res.headers["Content-Type"] = "application/json";
return false;
}
// Add user info to request
req.headers["X-User"] = tokenIt->second;
std::cout << "[AUTH] User authenticated: " << tokenIt->second << std::endl;
return true; // Continue to next middleware
}
void addToken(const std::string& token, const std::string& username) {
validTokens[token] = username;
}
};
// 5. CORS Middleware
class CORSMiddleware {
private:
std::string allowedOrigin;
std::vector<std::string> allowedMethods;
std::vector<std::string> allowedHeaders;
bool allowCredentials;
public:
CORSMiddleware(const std::string& origin = "*")
: allowedOrigin(origin), allowCredentials(true) {
allowedMethods = {"GET", "POST", "PUT", "DELETE", "OPTIONS"};
allowedHeaders = {"Content-Type", "Authorization", "X-Requested-With"};
}
bool operator()(HttpRequest& req, HttpResponse& res) {
// Add CORS headers to response
res.headers["Access-Control-Allow-Origin"] = allowedOrigin;
res.headers["Access-Control-Allow-Credentials"] = allowCredentials ? "true" : "false";
// Handle preflight request
if (req.method == "OPTIONS") {
res.headers["Access-Control-Allow-Methods"] = join(allowedMethods, ", ");
res.headers["Access-Control-Allow-Headers"] = join(allowedHeaders, ", ");
res.headers["Access-Control-Max-Age"] = "86400"; // 24 hours
res.statusCode = 204; // No Content
return false; // Stop pipeline
}
std::cout << "[CORS] Headers added for " << req.method << " " << req.path << std::endl;
return true;
}
private:
std::string join(const std::vector<std::string>& vec, const std::string& delim) {
std::string result;
for (size_t i = 0; i < vec.size(); ++i) {
if (i > 0) result += delim;
result += vec[i];
}
return result;
}
};
// 6. Content Type Middleware
class ContentTypeMiddleware {
public:
bool operator()(HttpRequest& req, HttpResponse& res) {
auto it = req.headers.find("Content-Type");
if (it != req.headers.end()) {
std::cout << "[CONTENT-TYPE] Request: " << it->second << std::endl;
}
// Set default response content type
if (res.headers.find("Content-Type") == res.headers.end()) {
res.headers["Content-Type"] = "application/json";
}
return true;
}
};
// 7. Rate Limiting Middleware
class RateLimitMiddleware {
private:
struct RequestCount {
int count;
std::chrono::system_clock::time_point resetTime;
};
std::map<std::string, RequestCount> requestCounts;
int maxRequests;
int windowSeconds;
public:
RateLimitMiddleware(int max = 100, int window = 60)
: maxRequests(max), windowSeconds(window) {}
bool operator()(HttpRequest& req, HttpResponse& res) {
// Get client identifier (IP address would be in real scenario)
std::string clientId = req.headers["X-Client-ID"];
if (clientId.empty()) {
clientId = "unknown";
}
auto now = std::chrono::system_clock::now();
auto it = requestCounts.find(clientId);
if (it == requestCounts.end()) {
// First request
requestCounts[clientId] = {1, now + std::chrono::seconds(windowSeconds)};
std::cout << "[RATE-LIMIT] " << clientId << ": 1/" << maxRequests << std::endl;
return true;
}
// Check if window has expired
if (now >= it->second.resetTime) {
it->second.count = 1;
it->second.resetTime = now + std::chrono::seconds(windowSeconds);
std::cout << "[RATE-LIMIT] " << clientId << ": Window reset" << std::endl;
return true;
}
// Increment count
it->second.count++;
if (it->second.count > maxRequests) {
res.statusCode = 429;
res.headers["Retry-After"] = std::to_string(windowSeconds);
res.headers["X-RateLimit-Limit"] = std::to_string(maxRequests);
res.headers["X-RateLimit-Remaining"] = "0";
res.body = R"({"error": "Rate limit exceeded"})";
res.headers["Content-Type"] = "application/json";
std::cout << "[RATE-LIMIT] " << clientId << ": LIMIT EXCEEDED" << std::endl;
return false;
}
int remaining = maxRequests - it->second.count;
std::cout << "[RATE-LIMIT] " << clientId << ": " << it->second.count
<< "/" << maxRequests << " (remaining: " << remaining << ")" << std::endl;
res.headers["X-RateLimit-Limit"] = std::to_string(maxRequests);
res.headers["X-RateLimit-Remaining"] = std::to_string(remaining);
return true;
}
};
// 8. Error Handling Middleware
class ErrorHandlingMiddleware {
public:
bool operator()(HttpRequest& req, HttpResponse& res) {
// This middleware wraps the handler to catch exceptions
return true; // Always continue
}
void handleError(std::exception& e, HttpResponse& res) {
res.statusCode = 500;
res.body = R"({"error": ")" + std::string(e.what()) + R"("})";
res.headers["Content-Type"] = "application/json";
std::cout << "[ERROR] " << e.what() << std::endl;
}
};
// 9. Compression Middleware
class CompressionMiddleware {
public:
bool operator()(HttpRequest& req, HttpResponse& res) {
// Check if client accepts compression
auto it = req.headers.find("Accept-Encoding");
if (it != req.headers.end()) {
// Check for gzip support
if (it->second.find("gzip") != std::string::npos) {
res.headers["Content-Encoding"] = "gzip";
std::cout << "[COMPRESSION] Gzip enabled" << std::endl;
// In real implementation, compress response body here
}
}
return true;
}
};
// 10. Security Headers Middleware
class SecurityHeadersMiddleware {
public:
bool operator()(HttpRequest& req, HttpResponse& res) {
res.headers["X-Content-Type-Options"] = "nosniff";
res.headers["X-Frame-Options"] = "DENY";
res.headers["X-XSS-Protection"] = "1; mode=block";
res.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains";
res.headers["Content-Security-Policy"] = "default-src 'self'";
std::cout << "[SECURITY] Security headers added" << std::endl;
return true;
}
};
// 11. Middleware Pipeline
class MiddlewarePipeline {
private:
std::vector<MiddlewareFunc> middlewares;
HandlerFunc finalHandler;
public:
MiddlewarePipeline& use(MiddlewareFunc middleware) {
middlewares.push_back(middleware);
return *this;
}
void setHandler(HandlerFunc handler) {
finalHandler = handler;
}
void process(HttpRequest& req, HttpResponse& res) {
// Execute middlewares in sequence
for (const auto& middleware : middlewares) {
if (!middleware(req, res)) {
// Middleware returned false, stop pipeline
return;
}
}
// All middlewares passed, execute final handler
if (finalHandler) {
finalHandler(req, res);
}
}
};
// Main demonstration
int main() {
std::cout << "=== Windows C++ HTTP Middleware Examples ===" << std::endl;
// Create middlewares
LoggingMiddleware logger;
AuthMiddleware auth;
CORSMiddleware cors;
ContentTypeMiddleware contentType;
RateLimitMiddleware rateLimiter(10, 60);
CompressionMiddleware compression;
SecurityHeadersMiddleware security;
// 1. Basic Pipeline
std::cout << "\n--- 1. Basic Pipeline ---" << std::endl;
MiddlewarePipeline pipeline1;
pipeline1.use(logger)
.use(cors)
.use(contentType)
.setHandler([](HttpRequest& req, HttpResponse& res) {
res.body = R"({"message": "Hello, World!"})";
std::cout << "[HANDLER] Request processed" << std::endl;
});
HttpRequest req1{"GET", "/api/test", {{"X-Client-ID", "client1"}}};
HttpResponse res1;
pipeline1.process(req1, res1);
std::cout << "Response: " << res1.statusCode << " - " << res1.body << std::endl;
// 2. Authenticated Pipeline
std::cout << "\n--- 2. Authenticated Pipeline ---" << std::endl;
MiddlewarePipeline authPipeline;
authPipeline.use(logger)
.use(auth)
.setHandler([](HttpRequest& req, HttpResponse& res) {
std::string user = req.headers["X-User"];
res.body = R"({"message": "Hello, )" + user + R"!("})";
});
HttpRequest authReq{"GET", "/api/profile", {{"Authorization", "Bearer abc123"}}};
HttpResponse authRes;
authPipeline.process(authReq, authRes);
std::cout << "Response: " << authRes.statusCode << " - " << authRes.body << std::endl;
// Failed auth
HttpRequest invalidAuthReq{"GET", "/api/profile", {{"Authorization", "Bearer invalid"}}};
HttpResponse invalidAuthRes;
authPipeline.process(invalidAuthReq, invalidAuthRes);
std::cout << "Response: " << invalidAuthRes.statusCode << " - " << invalidAuthRes.body << std::endl;
// 3. Full Stack Pipeline
std::cout << "\n--- 3. Full Stack Pipeline ---" << std::endl;
MiddlewarePipeline fullPipeline;
fullPipeline.use(security)
.use(cors)
.use(compression)
.use(rateLimiter)
.use(contentType)
.setHandler([](HttpRequest& req, HttpResponse& res) {
res.body = R"({"status": "success", "data": {"id": 123}})";
});
HttpRequest apiReq{"GET", "/api/data", {{"X-Client-ID", "app1"}, {"Accept-Encoding", "gzip, deflate"}}};
HttpResponse apiRes;
fullPipeline.process(apiReq, apiRes);
std::cout << "Response: " << apiRes.statusCode << std::endl;
// 4. Rate Limiting Test
std::cout << "\n--- 4. Rate Limiting Test ---" << std::endl;
MiddlewarePipeline rateLimitPipeline;
rateLimitPipeline.use(rateLimiter)
.setHandler([](HttpRequest& req, HttpResponse& res) {
res.body = R"({"message": "Request processed"})";
});
for (int i = 1; i <= 15; i++) {
HttpRequest rlReq{"GET", "/api/test", {{"X-Client-ID", "client2"}}};
HttpResponse rlRes;
rateLimitPipeline.process(rlReq, rlRes);
if (rlRes.statusCode == 429) {
std::cout << "Request " << i << ": Rate limited!" << std::endl;
break;
}
}
// Display logs
logger.displayLogs();
std::cout << "\n=== All HTTP Middleware Examples Completed ===" << std::endl;
return 0;
}
💻 Service de Fichiers Statiques cpp
🟡 intermediate
⭐⭐⭐⭐
Servir des fichiers statiques depuis le disque avec détection MIME, en-têtes de cache et support des requêtes de plage
⏱️ 35 min
🏷️ cpp, web, static files, http, windows
Prerequisites:
Intermediate C++, HTTP protocol, File I/O
// Windows C++ Static File Serving Examples
// Using Windows HTTP Server API
#include <iostream>
#include <string>
#include <map>
#include <fstream>
#include <sstream>
#include <windows.h>
#include <http.h>
#pragma comment(lib, "httpapi.lib")
// 1. MIME Type Detector
class MIMEDetector {
private:
std::map<std::string, std::string> mimeTypes;
public:
MIMEDetector() {
// Text types
mimeTypes[".html"] = "text/html";
mimeTypes[".htm"] = "text/html";
mimeTypes[".css"] = "text/css";
mimeTypes[".js"] = "application/javascript";
mimeTypes[".json"] = "application/json";
mimeTypes[".xml"] = "application/xml";
mimeTypes[".txt"] = "text/plain";
mimeTypes[".md"] = "text/markdown";
// Image types
mimeTypes[".jpg"] = "image/jpeg";
mimeTypes[".jpeg"] = "image/jpeg";
mimeTypes[".png"] = "image/png";
mimeTypes[".gif"] = "image/gif";
mimeTypes[".svg"] = "image/svg+xml";
mimeTypes[".ico"] = "image/x-icon";
mimeTypes[".webp"] = "image/webp";
mimeTypes[".bmp"] = "image/bmp";
// Font types
mimeTypes[".woff"] = "font/woff";
mimeTypes[".woff2"] = "font/woff2";
mimeTypes[".ttf"] = "font/ttf";
mimeTypes[".eot"] = "application/vnd.ms-fontobject";
// Video types
mimeTypes[".mp4"] = "video/mp4";
mimeTypes[".webm"] = "video/webm";
mimeTypes[".avi"] = "video/x-msvideo";
// Audio types
mimeTypes[".mp3"] = "audio/mpeg";
mimeTypes[".wav"] = "audio/wav";
mimeTypes[".ogg"] = "audio/ogg";
// Document types
mimeTypes[".pdf"] = "application/pdf";
mimeTypes[".doc"] = "application/msword";
mimeTypes[".docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
mimeTypes[".xls"] = "application/vnd.ms-excel";
mimeTypes[".xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
// Archive types
mimeTypes[".zip"] = "application/zip";
mimeTypes[".rar"] = "application/x-rar-compressed";
mimeTypes[".tar"] = "application/x-tar";
mimeTypes[".gz"] = "application/gzip";
}
std::string getMIMEType(const std::string& filePath) const {
size_t dotPos = filePath.find_last_of('.');
if (dotPos != std::string::npos) {
std::string ext = filePath.substr(dotPos);
auto it = mimeTypes.find(ext);
if (it != mimeTypes.end()) {
return it->second;
}
}
return "application/octet-stream"; // Default MIME type
}
void addMIMEType(const std::string& extension, const std::string& mimeType) {
mimeTypes[extension] = mimeType;
}
};
// 2. File Information
struct FileInfo {
std::string path;
std::string name;
size_t size;
FILETIME lastModified;
bool exists;
};
// 3. File Server
class StaticFileServer {
private:
std::string rootDirectory;
MIMEDetector mimeDetector;
std::map<std::string, std::string> customHeaders;
public:
StaticFileServer(const std::string& root) : rootDirectory(root) {
// Set default headers
customHeaders["Server"] = "CppFileServer/1.0";
customHeaders["X-Content-Type-Options"] = "nosniff";
}
void setRootDirectory(const std::string& path) {
rootDirectory = path;
}
void addHeader(const std::string& name, const std::string& value) {
customHeaders[name] = value;
}
// Get file information
FileInfo getFileInfo(const std::string& relativePath) {
FileInfo info;
std::string fullPath = rootDirectory + "\" + relativePath;
DWORD attrs = GetFileAttributesA(fullPath.c_str());
info.exists = (attrs != INVALID_FILE_ATTRIBUTES);
if (info.exists) {
info.path = fullPath;
info.name = getFileName(fullPath);
WIN32_FILE_ATTRIBUTE_DATA fileData;
if (GetFileAttributesExA(fullPath.c_str(), GetFileExInfoStandard, &fileData)) {
LARGE_INTEGER size;
size.HighPart = fileData.nFileSizeHigh;
size.LowPart = fileData.nFileSizeLow;
info.size = static_cast<size_t>(size.QuadPart);
info.lastModified = fileData.ftLastWriteTime;
}
}
return info;
}
// Read file content
std::vector<char> readFile(const std::string& path) {
std::ifstream file(path, std::ios::binary);
if (!file) {
return {};
}
file.seekg(0, std::ios::end);
size_t fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> content(fileSize);
file.read(content.data(), fileSize);
return content;
}
// Generate ETag
std::string generateETag(const FileInfo& info) {
// Use file size and last modified time
std::stringstream ss;
ss << """ << info.size << "-";
// Convert FILETIME to string
ULARGE_INTEGER uli;
uli.LowPart = info.lastModified.dwLowDateTime;
uli.HighPart = info.lastModified.dwHighDateTime;
ss << std::hex << uli.QuadPart << std::dec << """;
return ss.str();
}
// Format file time to HTTP date
std::string fileTimeToHTTPDate(const FILETIME& ft) {
FileTimeToLocalFileTime(&ft, const_cast<FILETIME*>(&ft));
SYSTEMTIME st;
FileTimeToSystemTime(&ft, &st);
char buffer[100];
sprintf_s(buffer, "%s, %02d %s %04d %02d:%02d:%02d GMT",
getDayName(st.wDayOfWeek).c_str(),
st.wDay,
getMonthName(st.wMonth).c_str(),
st.wYear,
st.wHour,
st.wMinute,
st.wSecond);
return buffer;
}
// Serve static file
struct FileResponse {
int statusCode;
std::map<std::string, std::string> headers;
std::vector<char> body;
std::string errorMessage;
};
FileResponse serveFile(const std::string& relativePath,
const std::string& ifNoneMatch = "",
const std::string& ifModifiedSince = "") {
FileResponse response;
response.statusCode = 200;
// Security check - prevent directory traversal
if (relativePath.find("..") != std::string::npos) {
response.statusCode = 403;
response.errorMessage = "Access denied";
return response;
}
// Handle directory - look for index.html
std::string requestPath = relativePath;
if (requestPath.empty() || requestPath.back() == '/') {
requestPath += "index.html";
}
FileInfo info = getFileInfo(requestPath);
if (!info.exists) {
response.statusCode = 404;
response.errorMessage = "File not found";
return response;
}
// Generate ETag
std::string etag = generateETag(info);
// Check ETag for conditional request
if (!ifNoneMatch.empty() && ifNoneMatch == etag) {
response.statusCode = 304; // Not Modified
return response;
}
// Add headers
response.headers["Content-Type"] = mimeDetector.getMIMEType(info.name);
response.headers["Content-Length"] = std::to_string(info.size);
response.headers["Last-Modified"] = fileTimeToHTTPDate(info.lastModified);
response.headers["ETag"] = etag;
response.headers["Cache-Control"] = "public, max-age=3600";
// Add custom headers
for (const auto& header : customHeaders) {
response.headers[header.first] = header.second;
}
// Read file
response.body = readFile(info.path);
if (response.body.empty() && info.size > 0) {
response.statusCode = 500;
response.errorMessage = "Failed to read file";
}
return response;
}
// Serve with range support (for video streaming)
FileResponse serveFileRange(const std::string& relativePath,
size_t rangeStart, size_t rangeEnd) {
FileResponse response;
response.statusCode = 206; // Partial Content
FileInfo info = getFileInfo(relativePath);
if (!info.exists) {
response.statusCode = 404;
return response;
}
// Validate range
if (rangeEnd >= info.size) {
rangeEnd = info.size - 1;
}
size_t contentLength = rangeEnd - rangeStart + 1;
// Add headers
response.headers["Content-Type"] = mimeDetector.getMIMEType(info.name);
response.headers["Content-Length"] = std::to_string(contentLength);
response.headers["Content-Range"] = "bytes " + std::to_string(rangeStart) +
"-" + std::to_string(rangeEnd) +
"/" + std::to_string(info.size);
response.headers["Accept-Ranges"] = "bytes";
// Read partial file
std::ifstream file(info.path, std::ios::binary);
if (file) {
file.seekg(rangeStart);
response.body.resize(contentLength);
file.read(response.body.data(), contentLength);
}
return response;
}
// List directory contents
std::vector<std::string> listDirectory(const std::string& relativePath) {
std::vector<std::string> entries;
std::string fullPath = rootDirectory + "\" + relativePath;
std::string searchPath = fullPath + "\*";
WIN32_FIND_DATAA fd;
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &fd);
if (hFind != INVALID_HANDLE_VALUE) {
do {
std::string name = fd.cFileName;
if (name != "." && name != "..") {
entries.push_back(name);
}
} while (FindNextFileA(hFind, &fd));
FindClose(hFind);
}
return entries;
}
private:
std::string getFileName(const std::string& path) {
size_t pos = path.find_last_of("\/");
if (pos != std::string::npos) {
return path.substr(pos + 1);
}
return path;
}
std::string getDayName(int day) {
static const char* days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
return days[day];
}
std::string getMonthName(int month) {
static const char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
return months[month - 1];
}
};
// 4. Cache Control Manager
class CacheManager {
public:
static std::string getCacheControl(const std::string& extension) {
// Static assets - long cache
if (extension == ".js" || extension == ".css" ||
extension == ".png" || extension == ".jpg" ||
extension == ".jpeg" || extension == ".gif" ||
extension == ".svg" || extension == ".ico" ||
extension == ".woff" || extension == ".woff2") {
return "public, max-age=31536000, immutable";
}
// HTML files - shorter cache for updates
if (extension == ".html" || extension == ".htm") {
return "public, max-age=3600, must-revalidate";
}
// Default
return "public, max-age=86400";
}
};
// 5. Gzip Compression Support
class CompressionHandler {
public:
static bool shouldCompress(const std::string& mimeType) {
// Compress text-based content
return mimeType.find("text/") == 0 ||
mimeType == "application/javascript" ||
mimeType == "application/json" ||
mimeType == "application/xml";
}
static std::vector<char> compressGzip(const std::vector<char>& data) {
// In real implementation, use zlib or similar
// This is a placeholder
std::cout << "[COMPRESSION] Gzip compression enabled" << std::endl;
return data; // Return uncompressed for demo
}
};
// Main demonstration
int main() {
std::cout << "=== Windows C++ Static File Serving Examples ===" << std::endl;
StaticFileServer server("./public");
// 1. Basic file serving
std::cout << "\n--- 1. Basic File Serving ---" << std::endl;
auto response1 = server.serveFile("index.html");
std::cout << "File: index.html" << std::endl;
std::cout << "Status: " << response1.statusCode << std::endl;
if (response1.statusCode == 200) {
std::cout << "Content-Type: " << response1.headers["Content-Type"] << std::endl;
std::cout << "Content-Length: " << response1.headers["Content-Length"] << std::endl;
std::cout << "ETag: " << response1.headers["ETag"] << std::endl;
} else {
std::cout << "Error: " << response1.errorMessage << std::endl;
}
// 2. MIME type detection
std::cout << "\n--- 2. MIME Type Detection ---" << std::endl;
MIMEDetector detector;
std::vector<std::string> testFiles = {
"index.html", "style.css", "app.js", "image.png",
"photo.jpg", "data.json", "font.woff2", "video.mp4"
};
for (const auto& file : testFiles) {
std::cout << file << " -> " << detector.getMIMEType(file) << std::endl;
}
// 3. Cache control
std::cout << "\n--- 3. Cache Control Headers ---" << std::endl;
std::cout << ".js files: " << CacheManager::getCacheControl(".js") << std::endl;
std::cout << ".css files: " << CacheManager::getCacheControl(".css") << std::endl;
std::cout << ".html files: " << CacheManager::getCacheControl(".html") << std::endl;
std::cout << ".png files: " << CacheManager::getCacheControl(".png") << std::endl;
// 4. Range requests
std::cout << "\n--- 4. Range Request Support ---" << std::endl;
auto rangeResponse = server.serveFileRange("video.mp4", 0, 1023);
std::cout << "Range: bytes 0-1023/..." << std::endl;
std::cout << "Status: " << rangeResponse.statusCode << std::endl;
std::cout << "Content-Range: " << rangeResponse.headers["Content-Range"] << std::endl;
// 5. ETag validation
std::cout << "\n--- 5. ETag Validation ---" << std::endl;
auto fileResponse = server.serveFile("index.html");
std::string etag = fileResponse.headers["ETag"];
std::cout << "Generated ETag: " << etag << std::endl;
// Simulate conditional request
auto cachedResponse = server.serveFile("index.html", etag);
std::cout << "Conditional request status: " << cachedResponse.statusCode << " (304 = Not Modified)" << std::endl;
// 6. Directory listing
std::cout << "\n--- 6. Directory Listing ---" << std::endl;
auto entries = server.listDirectory(".");
std::cout << "Files in root directory:" << std::endl;
for (const auto& entry : entries) {
std::cout << " - " << entry << std::endl;
}
// 7. Custom headers
std::cout << "\n--- 7. Custom Headers ---" << std::endl;
server.addHeader("X-Frame-Options", "DENY");
server.addHeader("X-XSS-Protection", "1; mode=block");
auto headerResponse = server.serveFile("page.html");
std::cout << "Response headers for page.html:" << std::endl;
for (const auto& header : headerResponse.headers) {
std::cout << " " << header.first << ": " << header.second << std::endl;
}
// 8. Compression check
std::cout << "\n--- 8. Compression ---" << std::endl;
std::vector<std::string> mimeTypes = {
"text/html", "application/json", "image/png", "video/mp4"
};
for (const auto& mime : mimeTypes) {
bool shouldCompress = CompressionHandler::shouldCompress(mime);
std::cout << mime << " - Compress: " << (shouldCompress ? "Yes" : "No") << std::endl;
}
std::cout << "\n=== All Static File Serving Examples Completed ===" << std::endl;
return 0;
}