# HN에 공개: C 웹 서버를 사용한 웹사이트 호스팅

> Clean Markdown view of GeekNews topic #16934. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=16934](https://news.hada.io/topic?id=16934)
- GeekNews Markdown: [https://news.hada.io/topic/16934.md](https://news.hada.io/topic/16934.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2024-09-26T09:51:20+09:00
- Updated: 2024-09-26T09:51:20+09:00
- Original source: [github.com/cozis](https://github.com/cozis/blogtech)
- Points: 2
- Comments: 1

## Topic Body

### My Blog Technology

이 웹 서버는 내 블로그를 호스팅하기 위해 설계된 최소한의 웹 서버임. 처음부터 공용 인터넷에 견딜 수 있도록 견고하게 제작되었음. 리버스 프록시가 필요하지 않음. 실제 동작하는 모습을 http://playin.coz.is/index.html에서 볼 수 있음. Reddit에 해킹을 요청하여 재미있고 악의적인 요청 로그를 기가바이트 단위로 수집했음. 일부는 `attempts.txt`에 저장했고, 나중에 재미로 더 찾아볼 예정임.

#### 하지만.. 왜?

나는 나만의 도구를 만드는 것을 즐기며, 모든 것이 "전투 테스트"되어야 한다는 말을 듣는 것에 지쳤음. 충돌이 발생하면 어쩔 것인가? 버그는 수정할 수 있음.

#### 사양

- Linux 전용
- HTTP/1.1, 파이프라이닝, keep-alive 연결 구현
- HTTPS 지원 (BearSSL을 사용하여 TLS 1.2까지)
- 최소한의 종속성 (HTTPS 사용 시 libc와 BearSSL)
- 구성 가능한 타임아웃
- 접근 로그, 충돌 로그, 로그 회전, 디스크 사용량 제한
- `Transfer-Encoding: Chunked` 없음 (`411 Length Required`로 응답하여 클라이언트가 `Content-Length`와 함께 다시 전송하도록 유도)
- 단일 코어 (더 나은 VPS를 얻으면 변경될 예정)
- 정적 파일 캐싱 없음 (아직)

#### 벤치마크

이 프로젝트의 초점은 견고성에 있지만, 결코 느리지 않음. nginx와의 간단한 비교 (정적 엔드포인트, 둘 다 단일 스레드, 1K 연결 제한):

- (blogtech) 
  ```bash
  $ wrk -c 500 -d 5s http://127.0.0.1:80/hello
  ```
  - 평균 대기 시간: 6.66ms
  - 요청/초: 76974.24
  - 전송/초: 6.09MB

- (nginx) 
  ```bash
  $ wrk -c 500 -d 5s http://127.0.0.1:8080/hello
  ```
  - 평균 대기 시간: 149.11ms
  - 요청/초: 44227.78
  - 전송/초: 8.27MB

nginx 설정:
```nginx
worker_processes 1;
events {
  worker_connections 1024;
}
http {
  server {
    listen 8080;
    location /hello {
      add_header Content-Type text/plain;
      return 200 "Hello, world!";
    }
  }
}
```

#### 빌드 및 실행

기본적으로 서버 빌드는 HTTP 전용임:
```bash
$ make
```
이 명령은 `serve` (릴리스 빌드), `serve_cov` (커버리지 빌드), `serve_debug` (디버그 빌드) 실행 파일을 생성함. 릴리스 빌드는 포트 80에서, 디버그 빌드는 포트 8080에서 청취함.

HTTPS를 활성화하려면 BearSSL을 클론하고 빌드해야 함:
```bash
$ mkdir 3p
$ cd 3p
$ git clone https://www.bearssl.org/git/BearSSL
$ cd BearSSL
$ make -j
$ cd ../../
$ make -B HTTPS=1
```
동일한 실행 파일이 생성되지만, 포트 443 (릴리스) 또는 8081 (디버그)에서 보안 연결이 가능함. `cert.pem`과 `key.pem` 파일을 실행 파일과 동일한 디렉토리에 배치해야 함. 이름과 위치를 변경하려면 다음을 수정:
```c
#define HTTPS_KEY_FILE "key.pem"
#define HTTPS_CERT_FILE "cert.pem"
```
로컬에서 HTTPS로 테스트하려면 자체 서명된 인증서(및 개인 키)를 생성:
```bash
openssl genpkey -algorithm RSA -out key.pem -pkeyopt rsa_keygen_bits:2048
openssl req -new -x509 -key key.pem -out cert.pem -days 365
```

#### 사용법

서버는 `docroot/` 폴더에서 정적 콘텐츠를 제공함. 이를 변경하려면 `respond` 함수를 수정:
```c
typedef struct {
  Method method;
  string path;
  int major;
  int minor;
  int nheaders;
  Header headers[MAX_HEADERS];
  string content;
} Request;

void respond(Request request, ResponseBuilder *b) {
  if (request.major != 1 || request.minor > 1) {
    status_line(b, 505); // HTTP Version Not Supported
    return;
  }

  if (request.method != M_GET) {
    status_line(b, 405); // Method Not Allowed
    return;
  }

  if (string_match_case_insensitive(request.path, LIT("/hello"))) {
    status_line(b, 200);
    append_content_s(b, LIT("Hello, world!"));
    return;
  }

  if (serve_file_or_dir(b, LIT("/"), LIT("docroot/"), request.path, NULLSTR, false))
    return;

  status_line(b, 404);
  append_content_s(b, LIT("Nothing here :|"));
}
```
여기에서 `request.path` 필드를 전환하여 엔드포인트를 추가할 수 있음. 경로는 요청 버퍼의 슬라이스일 뿐임. URI는 파싱되지 않음.

#### 테스트

서버를 valgrind와 sanitizers (주소, 정의되지 않음)로 정기적으로 실행하고 `wrk`를 사용하여 타겟팅함. 또한 HTTP/1.1 사양 준수를 확인하기 위해 `tests/test.py`에 자동화된 테스트를 추가하고 있음. 내 웹사이트를 호스팅하고 여기저기 게시하여 스트레스를 유지함. 인터넷에서 취약한 웹사이트를 스캔하는 모든 봇이 훌륭한 퍼저가 됨.

#### 알려진 문제

- 서버가 HTTP/1.0 클라이언트에 HTTP/1.1로 응답함

#### 기여

나는 주로 DEV 브랜치에서 작업하고 가끔 MAIN으로 병합함. 풀 리퀘스트를 열 때 DEV를 타겟으로 하면 더 쉬워질 것임.

### GN⁺의 정리

- 이 프로젝트는 최소한의 종속성과 견고성을 목표로 하는 웹 서버임.
- HTTP/1.1과 HTTPS를 지원하며, 다양한 로그 기능과 구성 가능한 타임아웃을 제공함.
- 벤치마크 결과 nginx보다 빠른 응답 시간을 보여줌.
- 개발자들이 자신의 도구를 만들고 버그를 수정하는 과정을 즐길 수 있도록 설계됨.
- 비슷한 기능을 가진 프로젝트로는 Nginx와 Apache HTTP Server가 있음.

## Comments



### Comment 29219

- Author: neo
- Created: 2024-09-26T09:51:20+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=41642151) 
- **역방향 프록시 필요 없음**: Jetty를 사용해 역방향 프록시 없이 앱을 인터넷에 배포해도 문제가 없었음
  - 보안이나 성능에 대한 구체적인 이유 없이 역방향 프록시를 사용하라는 의견이 많음
  - 역방향 프록시가 정말 필요한지 의문을 가짐

- **자체 제작한 C 웹 서버**: 상업용 웹사이트를 운영했던 C 웹 서버를 제작했음
  - 128MB RAM과 1 CPU로 많은 트래픽을 처리했음
  - 20년 전 인터넷 환경이 덜 적대적이었음을 언급함
  - 봇이 훌륭한 퍼저(fuzzer) 역할을 하지만 실제 퍼징도 필요함

- **서비스 구축의 만족감**: 시스템 API를 사용해 기본적인 서비스를 구축하는 것이 매우 만족스러움
  - poll() 함수가 높은 성능을 보여주는 것에 놀람
  - 연결별 함수와 관련 구조체, 배열이 nginx, redis, memcached와 유사함
  - 훌륭한 작업임

- **작은 프로젝트 소개**: 여가 시간에 시작한 재미있는 프로젝트를 소개함

- **Kore 프레임워크 추천**: C 앱을 작성할 때 공개된 부분을 작성하는 것이 불편하다면 Kore 프레임워크를 추천함
  - ACME 인증서 관리, Pgsql, curl, 웹소켓 등의 기능이 내장되어 있음
  - Lua/Python과 C를 혼합하여 모듈을 빌드하고 실행할 수 있음

- **흥미로운 링크 공유**: sqlite.org의 althttpd 인스턴스가 하루에 50만 개 이상의 HTTP 요청을 처리함
  - $40/월 Linode에서 200GB의 콘텐츠를 제공함
  - HTTP 요청의 19%가 CGI를 통해 Fossil 소스 코드 저장소에 접근함

- **자체 도구 제작의 즐거움**: 모든 것이 "전투 테스트"되어야 한다는 의견에 지침
  - 버그는 수정할 수 있음

- **Chaos Communication Congress 강연**: 보안 기능이 포함된 C로 작성된 블로그/웹 서버에 대한 강연을 상기시킴
  - 불변 저장소, 권한 축소, TLS 인증서 접근 불가 등의 기능이 포함됨

- **안정적인 웹사이트**: 첫 페이지에 표시되어도 크래시하지 않는 웹사이트

- **기본으로 돌아가기**: 필요한 것만 사용하여 기본으로 돌아가는 접근 방식을 좋아함
  - 소프트웨어의 불필요한 기능이 성능에 미치는 영향을 의문시함
  - 개발자에게 축하 인사를 전함
