스크라이어 프롤로그로 작성된 젬미니 프로토콜 서버, Sula
Sula는 Scryer Prolog 언어로 작성된 Gemini 프로토콜 서버입니다. TLS 통신과 인증서 검증을 지원하며, 향후 CGI 스크립트 실행 및 다중 스레딩 등 다양한 기능 추가를 계획하고 있는 오픈소스 프로젝트입니다. 개발자 취향의 가벼운 마이크로 서비스나 테스트용 서버를 구축하고자 하는 실무자들에게 유용하게 쓰일 수 있습니다.
목차
Sula라는 이름은 스페인어로 캡슐을 의미하는 Cápsula의 접미사에서 따왔습니다.
Git
Scryer Prolog로 작성된 Gemini 프로토콜 서버입니다.
요구 사항
sula는 패치된 Scryer Prolog에 의존하며, 패치된 버전은 여기(js/fixes 브랜치)에서 찾을 수 있습니다. 필요한 패치는 다음과 같습니다:
Prolog 힙(Heap)에 데이터를 구체화(materialising)하지 않고 바이너리 파일 본문을 TLS 클라이언트로 스트리밍하기 위해 사용되는 네이티브 '$copy_stream'/2 내장 함수. 프로세스 파이프(및 at_end_of_stream/1이 true를 반환하지 않는 다른 스트림)에서 EOF(파일 끝) 도달 시 무한 루프에 빠지지 않고 정상적으로 종료되도록 하는 library(pio)의 buffer_prepare_for_n/5 수정 사항. Scryer의 INTERRUPT 플래그를 확인하는 논블로킹 폴링 루프가 적용된 socket_server_accept/4. 이를 통해 SIGINT가 블로킹 시스템 호출 뒤에 갇히지 않고, catch 가능한 '$interrupt_thrown' 예외로 처리됩니다. rustls로의 포팅. 선택적 클라이언트 인증서를 포함하도록 수정된 tls_server_negotiate.
패치된 Scryer를 빌드하고 설치하는 방법:
git clone https://git.sagredo.dev/scryer-prolog -b js/fixes cd scryer-prolog cargo install --path .
또한 openssl(1)이 PATH에 설정되어 있어야 합니다. 이는 시작 시 구성된 ID 인증서에서 CN(Common Name)을 읽어 구성된 호스트 이름과 일치하는지 확인하기 위해 호출됩니다.
실행 방법
./sula.pl --addr HOST:PORT --hostname NAME --content DIR --certs DIR
sula.pl은 폴리글랏(Polyglot) 스크립트입니다. bash는 PATH에서 scryer-prolog를 감지하고, 엔트리 골(Entry goal)로 sula:run, halt를 지정하여 실행합니다.
예시:
./sula.pl
--addr 127.0.0.1:1965
--hostname gmi.example.dev
--content ./site
--certs .
CLI 옵션
모든 옵션은 순서에 상관없이 받아들입니다. 인식할 수 없는 옵션은 조용히 무시됩니다.
옵션 | 의미 | 기본값 --addr HOST:PORT | 리스닝 소켓의 바인딩 주소 및 포트 | 127.0.0.1:1965 --hostname NAME | 인증서의 예상 CN. 일치하지 않으면 시작 중단 | localhost --content DIR | 서비스할 파일들의 루트 디렉토리 | ./site --certs DIR | cert.pem 및 key.pem이 포함된 디렉토리 |
서버 중지
Ctrl+C는 깔끔한 종료를 유발합니다. 리스닝 소켓이 닫히고, 최상위 catch 블록이 'Shutting down'을 로깅하며, 프로세스가 종료 코드 0번으로 종료됩니다.
주요 기능
rustls를 통한 TLS 및 PKCS#12 ID 파일 지원.
호스트 이름 검증: 시작 시 cert_is_for_hostname/2가 openssl x509를 실행하여 인증서의 CN이 --hostname과 일치하는지 확인합니다. mime/2를 통한 확장자 기반 콘텐츠 협상. 이는 시작 시 /etc/mime.types(DCG를 통해 mime.pl에서 파싱됨)에서 불러옵니다. .gmi 파일에 대해서는 text/gemini가 추가됩니다. 텍스트 응답은 format/3을 통해 전송됩니다. 바이너리 응답은 copy_stream/2를 통해 네이티브 코드로 스트리밍됩니다(파일 → TLS 소켓, Prolog 힙 트래픽 없음). 연결별 오류 처리: TLS 핸드셰이크 실패 및 스트리밍 중 클라이언트 연결 끊김은 로그에 기록되고 루프가 계속됩니다. 다른 오류는 다시 throw되어 최상위에 표시됩니다. 패치된 Scryer socket_server_accept/4를 통한 SIGINT 그레이스풀 종료(Graceful shutdown) 지원.
프로젝트 구조
sula.pl: 폴리글랏 런처 및 메인 sula 모듈(run/0, 요청 루프).
config.pl: CLI 파싱(DCG) 및 config 접근자(cert/1, addr/1 등). cert.pl: 인증서 로딩 + 호스트 이름-CN 일치 확인. mime.pl: /etc/mime.types 파서(DCG) 및 mime/2 팩트. request.pl: 요청 라인 리더. gemini_uri.pl: Gemini URI DCG(gemini://host[:port]/path[?query]). ip.pl: IP 주소 인식(Gemini 호스트로는 거부됨). response.pl: 응답 상태 코드 DCG. log.pl: 태그가 지정된 log_msg/3. banner.pl: banner.txt를 읽고 display_banner/1을 통해 라인별로 출력.
추후 계획된 기능
identity.p12 대신 key 및 cert 직접 사용
클라이언트 인증서 구성 파일에서 설정 로드 사용자 저장 및 로드 CGI 스크립트 실행 모든 상태 코드 구현 요청 속도 제한(Rate limiting) 가상 호스팅 파일 로깅 다중 스레딩(Multi-threading) 또는 이와 유사한 기능 핫 리로드(Hot reload)