``` 基于我的自定义爱好操作系统的 C++ Web 服务器 ```
C++ Web Server on my custom hobby OS

原始链接: https://oshub.org/projects/retros-32/posts/getting-a-webserver-running

经过长时间的中断,一个业余操作系统项目重新启动开发,最终实现了一个可用的Web服务器。该项目最初专注于构建完整的网络协议栈(以太网、IP、TCP/UDP、DHCP、DNS),其中TCP的实现尤其具有挑战性,需要调试损坏的终端缓冲区和E1000驱动问题。 网络稳定后,实现了一个自定义的HTTP引擎,利用了另一个项目的现有HTTP解析器。该引擎支持使用lambda函数处理程序的简单路由,模仿了现代Web框架的方法。添加了一个带有缓存的文件存储库,用于提供静态内容。 该服务器现在可以成功处理请求、提供文件,甚至可以处理TCP RST数据包的突发——这是浏览器刷新行为的关键修复。未来的计划包括更完善的用户界面、优雅的关闭功能,以及雄心勃勃的构建操作系统Web浏览器的任务。这一里程碑标志着一个长期、从零开始的业余项目取得了重大进展。

Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 C++ Web 服务器在我的自定义爱好操作系统上 (oshub.org) 9 分,作者 joexbayer 16 分钟前 | 隐藏 | 过去 | 收藏 | 2 条评论 roetlich 11 分钟前 [–] 很酷!为什么以及整个网络协议栈都很直接,但 HTTP(和 TCP)却不是呢? 你能从其他项目中获取灵感来处理像 DNS 这样的事情吗?回复 joexbayer 9 分钟前 | 父评论 [–] 在 TCP 之前,大多数协议都非常直接,至少让它们半可靠地工作。但 TCP 由于所有状态管理和连接可能采取的路径而变得非常复杂。HTTP 主要是因为所有文本解析而令人恼火:D 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

After a long break from working on my hobby operating system, I finally got back into it and finished a very important milestone: a working web server.

Web browser accessing HTTP server on my OS

Networking was always integral to my hobby project. The first goal was getting the basic networking stack working: Ethernet, IP, ARP, UDP, TCP, DHCP and DNS. Besides TCP this was rather straightforward, but when moving onto HTTP things broke.

First time getting TCP to work.

Networking stack code

This led to my first break from the project, but also left a nagging thought in my mind, wanting to make it work. I finally sat down and started debugging. 

I eventually found the culprit after hours of dissecting my own code, the problem was a broken implementation of the terminal buffer, overwriting a lock in another process… fun. Additionally, the E1000 network driver did not correctly handle incoming packets, which I finally got working by handling bursts of packets.


After getting an HTML page returned from the web engine I started noticing lots of performance errors and hangs from TCP, mainly because quickly refreshing the browser led to a spam of RST packets which were not handled correctly.

After a few hours of tinkering I finally got the RST packets working and the network stack is now able to handle a packet spam from the browser.

Next step was actually implementing a HTTP engine, parsing the requests from the user. Before this engine I simply returned a static HTTP response no matter the actual request.

Keeping with the spirit of this hobby OS I want to write everything from scratch, luckily I already had implemented a pretty complete HTTP parser for my other project c-web-modules. So I extracted the HTTP parser as a standalone library and ported it to my OS.


After the HTTP engine was done I moved onto the web engine, focusing on something small, rather than big and fancy. Mainly routing was important and adding route handlers. Allowing the user to specify a route, method and lambda function handler.
/* Simple routing */
engine.get("/", [](const http::Request& req, http::Response& res) {
    (void)req;
    res.setBody("hello world!");
    ....
});

It’s a tiny example, but it mirrors how a lot of modern C++ and web frameworks think about routing: match a path + method, call a handler, build a response.
#lang:plaintext
[ Browser ]
    |
    v
[ Web Server (userspace):
  WebEngine | HTTPEngine | FileRepository ]
    |
    v
[ Network stack:
  TCP/UDP | IP | ARP | DHCP | DNS | Ethernet(E1000) ]

The last step was updating the userspace program with the new HTTP and Web engine. Finally I added a way to serve files using a FileRepository which supports caching. Now I can edit the files inside the operating system and then serve them with the web server.
#lang:cpp
WebEngine webEngine(80, 16);
web::FileRepository fileRepo;

/* Simple static pages */
webEngine.get("/home", [&fileRepo](const http::Request& req, http::Response& res) {
    (void)req;
    res.sendFile(fileRepo, "/web/index.htm");
});

webEngine.get("/about", [&fileRepo](const http::Request& req, http::Response& res) {
    (void)req;
    res.sendFile(fileRepo, "/web/about.htm");
});

webEngine.get("/status", [&fileRepo](const http::Request& req, http::Response& res) {
    (void)req;
    res.sendFile(fileRepo, "/web/status.htm");
});

webEngine.run();


The next thing on the TODO list will be to add a more fancy UI for the webserver and a way to close it gracefully.

(Graceful shutdown is one of those “boring” features you don’t miss… until the first time you corrupt something on exit.)


When this is finished, the biggest task so far will begin… the web browser.

联系我们 contact @ memedata.com