Elysia Tools
Navigation mobile
C++
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
Exemples
Entrées de cette collection
Routage URL
Implémenter l'analyse et la correspondance des routes URL avec paramètres de chemin et chaînes de requête
Difficulté
7/10
Temps estimé
35 min
Étiquettes
cpp, web, routing, http, windows
Prérequis
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 <functional>
#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
Implémenter un pipeline de middleware de traitement des requêtes pour la journalisation, l'authentification et la gestion CORS
Difficulté
8/10
Temps estimé
40 min
Étiquettes
cpp, web, middleware, http, windows
Prérequis
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
Servir des fichiers statiques depuis le disque avec détection MIME, en-têtes de cache et support des requêtes de plage
Difficulté
7/10
Temps estimé
35 min
Étiquettes
cpp, web, static files, http, windows
Prérequis
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;
}Outils
Outils souvent utilisés avec cet exemple
Associé