เปิดตัวบน HN: โฮสต์เว็บไซต์ด้วยเว็บเซิร์ฟเวอร์ภาษา C
(github.com/cozis)- BlogTech เป็นชุดเครื่องมือบนภาษา C สำหรับจัดการเว็บไซต์ขนาดเล็กถึงขนาดกลาง โดยมี HTTPS, virtual host, การจัดการใบรับรองอัตโนมัติผ่าน ACME และไคลเอนต์สำหรับจัดการเซิร์ฟเวอร์ระยะไกล
- ปัจจุบันยังอยู่ใน ขั้นทดสอบ เวอร์ชัน
0.4.xเคยถูกใช้รันบริการเป็นเวลาหลายสัปดาห์แล้ว แต่ไม่ได้ถูกนำเสนอว่าเป็น stable release - เซิร์ฟเวอร์รันได้บน Linux และ Windows แต่ HTTPS รองรับเฉพาะ Linux และบน Windows ทั้งไคลเอนต์และเซิร์ฟเวอร์ใช้ได้เฉพาะ HTTP
- การจัดการไฟล์ระยะไกลทำผ่าน
--put,--get,--deleteและคำขอที่เปลี่ยนแปลงทรัพยากรจะยืนยันตัวตนด้วย HMAC/SHA256 signature ตาม shared secret key - ในสภาพแวดล้อม production ต้องมี HTTP server ที่พอร์ต 80 และระเบียน DNS ของโดเมน เมื่อเปิด ACME จะสร้างไฟล์ใบรับรองและ private key โดยอัตโนมัติ และบันทึกข้อผิดพลาดไว้ใน
acme.log
เป้าหมายและสถานะปัจจุบันของ BlogTech
- BlogTech เป็นชุดเครื่องมือสำหรับจัดการเว็บไซต์ขนาดเล็กถึงขนาดกลาง
- ฟีเจอร์ที่รองรับมีดังนี้
-
HTTPS
-
Virtual host
- การจัดการใบรับรองอัตโนมัติ ผ่าน ACME
- ไคลเอนต์ สำหรับจัดการเซิร์ฟเวอร์ระยะไกล
- ยังอยู่ใน ขั้นทดสอบ
- มีกรณีที่เวอร์ชัน
0.4.xให้บริการhttps://coz.is/ได้สำเร็จเป็นเวลาหลายสัปดาห์ - เวอร์ชันไฟล์เดี่ยวแบบเดิมอยู่ใน branch
single_file
-
การ build และการรันพื้นฐาน
- BlogTech รันได้บน Linux และ Windows
- การ build บน Linux ต้องมี OpenSSL development libraries และ
gcc - การ build บน Windows ต้องมี
clang
- การ build บน Linux ต้องมี OpenSSL development libraries และ
- การ build ทำผ่านสคริปต์ตามแพลตฟอร์ม
- Linux:
./build.sh - Windows:
.\build.bat
- Linux:
- ผลลัพธ์จากการ build มีดังนี้
- Linux:
blogtech - Windows:
blogtech.exe
- Linux:
- บน Linux สามารถติดตั้งได้ด้วย
./install.sh - ตัวอย่างการรันเซิร์ฟเวอร์พื้นฐานคือสร้างไดเรกทอรี
docrootแล้วระบุ--serve,--document-root=docroot,--skip-auth-check - HTTP server พื้นฐานจะ listen ที่
127.0.0.1:8080และให้บริการคอนเทนต์จากdocroot - เมื่อ
docrootว่าง การเข้าถึงhttp://127.0.0.1:8080/จะคืนค่า 404
การจัดการไฟล์ระยะไกลและการยืนยันตัวตน
- BlogTech สามารถอัปโหลดไฟล์ไปยังเซิร์ฟเวอร์ในโหมดไคลเอนต์ได้
- อัปโหลดไฟล์ด้วย
--put - ดาวน์โหลดไฟล์ระยะไกลด้วย
--get - ลบไฟล์ระยะไกลด้วย
--delete
- อัปโหลดไฟล์ด้วย
- ไม่ว่าจะรันไคลเอนต์และเซิร์ฟเวอร์บนเครื่องเดียวกัน หรือกับเซิร์ฟเวอร์บนเครื่องระยะไกล ก็ทำงานในลักษณะเดียวกัน
- คำขอที่เปลี่ยนแปลงทรัพยากรจะถูกเซ็นแบบดิจิทัลด้วย HMAC/SHA256 signature
- ไคลเอนต์และเซิร์ฟเวอร์ต้องใช้ secret key เดียวกันร่วมกัน โดยตามธรรมเนียมจะเก็บไว้ในไฟล์
admin.pwd - ระบุไฟล์รหัสผ่านด้วย option
--auth-password-file - หากไม่มีไฟล์รหัสผ่าน คำขอทั้งหมดที่ต้องยืนยันตัวตนจะถูกปฏิเสธ
- รหัสผ่านว่างก็จะถูกปฏิเสธเช่นกัน
- ระหว่างพัฒนา สามารถใช้
--skip-auth-checkเพื่อถือว่าคำขอทั้งหมดผ่านการยืนยันตัวตนแล้วได้- เมื่อใช้ option นี้ แม้จะระบุ
--auth-password-fileไว้ก็จะถูกละเว้น - ควรใช้อย่างระมัดระวัง
- เมื่อใช้ option นี้ แม้จะระบุ
-
เงื่อนไขของ header ยืนยันตัวตนและการส่งซ้ำ
- HTTP request ที่ยืนยันตัวตนแล้วจะมี header
X-BlogTech- X-BlogTech-Nonce: token ที่ไคลเอนต์สุ่มเลือกX-BlogTech-Timestamp: UNIX timestamp ณ เวลาที่คำขอถูกเซ็นครั้งแรกX-BlogTech-Expire: ระยะเวลาเป็นวินาทีที่คำขอยังคงมีผล นับจากเวลาที่เซ็นX-BlogTech-Signature: ค่า HMAC ของข้อมูลคำขอที่เข้ารหัสเป็น Base64- signature ได้มาจากการสร้างสตริงคำขอแบบ normalized จากนั้นคำนวณ HMAC/SHA256 โดยใช้รหัสผ่านสำหรับยืนยันตัวตนเป็น key แล้วเข้ารหัสผลลัพธ์เป็น Base64 พร้อม padding
- nonce ที่เซิร์ฟเวอร์เคยเห็นแล้วจะถูกเก็บไว้ใน หน่วยความจำ
- เมื่อเซิร์ฟเวอร์ reload จะลืม nonce ก่อนหน้า ดังนั้นคำขอที่ยังไม่หมดอายุอาจถูกส่งซ้ำได้
- HTTP request ที่ยืนยันตัวตนแล้วจะมี header
Virtual host และการ mapping ไดเรกทอรี
- BlogTech สามารถโฮสต์หลายเว็บไซต์บนเซิร์ฟเวอร์เดียวกันได้
- ตั้งค่าโดเมนด้วยการระบุ option
--domainหลายครั้ง - BlogTech จะสร้างไดเรกทอรีแยกตามโดเมนภายใน document root
defaultwebsiteA.comwebsiteB.com
- คำขอที่เข้ามายัง host เฉพาะจะอ้างอิงไดเรกทอรีที่เชื่อมโยงกับ host นั้น
- คำขอที่ไม่ได้เชื่อมโยงกับโฟลเดอร์เฉพาะจะอ้างอิงไดเรกทอรี default
- ในตัวอย่าง ไฟล์ที่อัปโหลดไปยัง
websiteA.com,websiteB.com,other.comจะถูกบันทึกไว้ในไดเรกทอรีของโดเมนแต่ละรายการหรือไดเรกทอรีdefault
HTTPS และการตั้งค่าใบรับรอง
- HTTPS รองรับเฉพาะบน Linux
- เนื่องจาก cHTTP ซึ่งเป็น HTTP library เบื้องหลัง implement HTTPS ด้วย OpenSSL
- บน Windows ทั้งไคลเอนต์และเซิร์ฟเวอร์ใช้ได้เฉพาะ HTTP
- หากต้องการเปิด HTTPS ต้องมี option ต่อไปนี้
--https-enabled--cert-file--cert-key-file
- ที่อยู่ listen เริ่มต้นของ HTTPS คือ
127.0.0.1:8443 - สามารถเปลี่ยนที่อยู่และพอร์ตที่ listen ได้ด้วย option ต่อไปนี้
--https-addr=<addr>--https-port=<port>
- ระหว่างพัฒนา สามารถสร้าง ใบรับรองแบบ self-signed ด้วยคำสั่ง OpenSSL ได้
- เบราว์เซอร์ไม่อนุญาตให้ท่องเว็บเซิร์ฟเวอร์ HTTPS ที่ใช้ใบรับรองแบบ self-signed
- cURL ต้องใช้ flag
--insecureจึงจะเข้าถึงเซิร์ฟเวอร์ที่ใช้ใบรับรองแบบ self-signed ได้ - หากต้องการใบรับรองหลายใบ สามารถส่ง
--extra-certเพิ่มได้- ใบรับรองหลักระบุด้วย
--cert-file,--cert-key-file - ใบรับรองเพิ่มเติมจะถูกใช้เมื่อไคลเอนต์ร้องขอโดเมนเฉพาะ
- ใบรับรองหลักระบุด้วย
- option บรรทัดคำสั่งที่ยาวสามารถย้ายไปไว้ในไฟล์ config ได้
การออกใบรับรองอัตโนมัติผ่าน ACME
- โปรโตคอล ACME ช่วยให้เว็บเซิร์ฟเวอร์ขอออกใบรับรองจาก Certificate Authority ได้โดยอัตโนมัติ
- BlogTech รองรับ flow ที่เริ่มต้นในโหมด HTTPS โดยยังไม่มีใบรับรอง แล้วจึงสร้างใบรับรองขึ้นมา
- ในสภาพแวดล้อม production ต้องมีเงื่อนไขต่อไปนี้
- รัน HTTP server ที่ พอร์ต 80
- สร้าง DNS record ที่เชื่อมโดเมนเข้ากับเครื่องที่รันเซิร์ฟเวอร์
- เปิด ACME โดยระบุ
--acme-enabledร่วมกับ option ของ HTTPS - ต่างจากโหมด HTTPS ทั่วไป ในโหมด ACME จะคาดว่าใบรับรองยังไม่มีอยู่
- การสร้างใบรับรองต้องใช้ค่าต่อไปนี้
--acme-domain--acme-email--acme-country--acme-organization--acme-agree-tos
- หากประมวลผลสำเร็จ จะสร้างไฟล์ต่อไปนี้
acme_key.pem: private key ที่เชื่อมโยงกับบัญชี ACMEcert.pem: ใบรับรองที่ออกให้ หรือไฟล์ที่ระบุด้วย--cert-filekey.pem: private key ที่เชื่อมโยงกับใบรับรอง หรือไฟล์ที่ระบุด้วย--cert-key-file
- หากเกิดข้อผิดพลาด ข้อความจะถูกบันทึกไว้ใน
acme.log - หากระบุ
--acme-domainหลายครั้ง จะสามารถให้ ACME จัดการหลายโดเมนได้ - ใบรับรองผลลัพธ์จะรวมโดเมนทั้งหมดที่ระบุไว้
การทดสอบไคลเอนต์ ACME
- หากต้องการทดสอบไคลเอนต์ ACME บน Linux ต้องติดตั้ง Docker และ clone Pebble ACME server
- ใน
docker-compose.ymlให้เพิ่มรายการextra_hostsที่เชื่อมโดเมนทดสอบไปยังhost-gateway - ใน
/etc/hostsให้ map โดเมนทดสอบไปยัง127.0.0.1 - เริ่ม Pebble ด้วย
docker compose up - BlogTech รัน instance สำหรับทดสอบด้วย
./blogtech -s --config=misc/pebble_blogtech.conf - การโต้ตอบกับ ACME server จะแสดงออกทาง stdout
- หากสำเร็จ จะสร้าง
acme_key.pem,cert.pem,key.pem - ทดสอบการต่ออายุใบรับรองได้โดยเปลี่ยนค่า
validityPeriodในpebble/test/config/pebble-config.json - สามารถใช้ option
--acme-force-renewal-period=<maximum duration in ms>เพื่อสั่งให้ต่ออายุ แม้ใบรับรองยังไม่หมดอายุก็ตาม
ไฟล์ config และการโหลดอัตโนมัติ
- BlogTech สามารถย้าย command-line arguments จำนวนเท่าใดก็ได้ไปไว้ใน ไฟล์ config
- ตัวอย่างเช่น เขียน option ที่เกี่ยวข้องกับ HTTPS และ ACME ไว้ใน
blogtech_server.confแล้วรันด้วย--config=blogtech_server.conf - หากชื่อไฟล์ config เป็น
blogtech.confพอดี BlogTech จะโหลดโดยอัตโนมัติ - ในกรณีนี้สามารถรันแบบ
./blogtech --serveได้ - หากต้องการละเว้นการโหลดไฟล์ config แบบ implicit ให้ใช้
--no-config
Crash log และ systemd daemon
- หาก BlogTech crash ในโหมดเซิร์ฟเวอร์ จะสร้างไฟล์
crash.bin - เมื่อเริ่มเซิร์ฟเวอร์ครั้งถัดไป
crash.binจะถูกแปลงเป็นcrash.logซึ่งเป็น stack trace ที่มนุษย์อ่านได้ - ขั้นตอนการแปลง address เป็นชื่อ symbol หรือเลขบรรทัดอาจยังไม่เสถียรนัก
- BlogTech สามารถติดตั้งเป็น systemd daemon ได้
- ตัวอย่าง
blogtech.serviceจะรัน/root/blogtech/blogtech -sรีสตาร์ทเมื่อ fail และตั้ง working directory เป็น/root/blogtech/ - option ของเซิร์ฟเวอร์จะถูกโหลดอัตโนมัติจาก
/root/blogtech/blogtech.conf - หลังคัดลอก service file ไปยัง
/etc/systemd/system/แล้ว ให้เปิดใช้งานและเริ่มด้วยคำสั่งต่อไปนี้systemctl daemon-reloadsystemctl enable blogtechsystemctl start blogtech
- จัดการ service ได้ด้วย
systemctl start,systemctl stop,systemctl restart,journalctl -u blogtech
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ส่วนที่ว่า “ไม่จำเป็นต้องมี reverse proxy” ทำให้ผมสงสัยอยู่เสมอ
ถ้าไม่มีเหตุผลพิเศษที่ reverse proxy จะช่วยได้ ผมก็มักจะเปิดแอป Jetty แบบ embedded ออกสู่อินเทอร์เน็ตโดยตรงโดยไม่มีอะไรอยู่ข้างหน้า และก็ไม่เคยมีปัญหา
คนดูแลอินฟราหรือความปลอดภัยมักถามว่าทำไมไม่วาง nginx ไว้ข้างหน้า แต่พอถามเหตุผล ก็มักพูดกว้าง ๆ เรื่องความปลอดภัยหรือประสิทธิภาพ โดยขาดความชัดเจน คำตอบที่เป็นรูปธรรมที่สุดคือ slow loris แต่เรื่องนั้นก็ไม่ใช่ปัญหาใหญ่มานานแล้ว
ผมสงสัยว่า reverse proxy กลายเป็น cargo cult ในหมู่คนทำงานไปแล้วหรือเปล่า หรือผมพลาดเหตุผลดี ๆ ที่ใช้ได้ทั่วไปอยู่
โดยรวมแล้วช่วยปกป้องเซิร์ฟเวอร์ต้นทางและให้รับเฉพาะทราฟฟิกที่เกี่ยวข้องได้ แม้ในกรณีให้โดเมนแบบกำหนดเองแก่ลูกค้า DNS ของลูกค้าก็จะชี้ไปที่ reverse proxy ไม่ใช่เซิร์ฟเวอร์ต้นทาง ดังนั้นถ้าจำเป็นต้องเปลี่ยนเซิร์ฟเวอร์ต้นทาง ก็ไม่ต้องกังวลเรื่องให้ลูกค้าแก้ DNS
แต่ละตัวรันบนพอร์ตต่างกัน แต่ผมอยากให้ทั้งหมดเข้าถึงจากสาธารณะได้ด้วย URL คนละอัน และอยากเปิดเผยต่ออินเทอร์เน็ตแค่ พอร์ต 443
ยังอยากให้ต่ออายุใบรับรอง TLS ของแต่ละโดเมนอัตโนมัติด้วย ความต้องการข้อแรกต้องใช้ reverse proxy และ Caddy ทำได้ทั้งสองอย่าง
ถ้ารันแค่เซิร์ฟเวอร์เดียว และเซิร์ฟเวอร์นั้นทำ TLS termination เองอยู่แล้ว reverse proxy ก็ไม่จำเป็นเสมอไป
ภายหลังสามารถเพิ่ม TLS termination, URL rewrite และงานอื่น ๆ ได้โดยไม่ต้องลงแรงมาก เลยใช้จนเป็นนิสัย และจนถึงตอนนี้นิสัยนั้นก็ตอบแทนผมดี
ด้านความปลอดภัย เราอยากให้ทั้งระบบมีโค้ดให้น้อยที่สุดเท่าที่ทำได้ และใช้ระบบปฏิบัติการที่แข็งแกร่ง แอปแบบพร็อกซีอาจเรียบง่ายกว่าเว็บ/แอปพลิเคชันเซิร์ฟเวอร์มาก และยังสามารถกรองทราฟฟิกขาเข้า ตรวจสอบอินพุต หรือแปลงให้อยู่ในรูปแบบที่ parse ได้ปลอดภัยและเร็วกว่าได้ด้วย อาจรันบนระบบปฏิบัติการที่โจมตียากอย่าง OpenBSD, GenodeOS, INTEGRITY-178B ก็ได้
ด้านความพร้อมใช้งาน การวาง load balancing, monitoring และ recovery ไว้ในระบบเหล่านี้ก็มักปลอดภัยกว่า เพราะแอปพลิเคชันเซิร์ฟเวอร์อาจล่มบ่อยกว่า
ด้านประสิทธิภาพ ข้อดีแรกคือสามารถปรับแต่งแอปที่เรียบง่ายและมีหน้าที่เฉพาะให้เหมาะสมได้อย่างมาก จากนั้นอาจใช้ตัวเร่งฮาร์ดแวร์ CPU หรือ PCI เพื่อเร่งการบีบอัดหรือการเข้ารหัส ซึ่งมักเรียกว่า offloading โครงสร้างที่คุ้มค่าที่สุดคือให้เซิร์ฟเวอร์ทั่วไปจำนวนมากได้รับประโยชน์จากเซิร์ฟเวอร์ offloading ราคาแพงจำนวนน้อย บางส่วนใช้ load balancing เพื่อส่งทราฟฟิกขาเข้าไปยังเซิร์ฟเวอร์ที่รับมือได้ดีที่สุด ลดการใช้ทรัพยากรราคาแพง
ในคอนฟิกขั้นต่ำสุดอาจไม่จำเป็น แต่แม้ในสภาพแวดล้อมที่รันบนโฮสต์เดียว ก็ช่วยให้ทำ rolling release, การบีบอัด, TLS, การเสิร์ฟไฟล์ static อย่างรวดเร็ว และ A/B testing ที่อาจต้องใช้ได้
ชั้นคั่นกลางระหว่าง request กับเซิร์ฟเวอร์มีประโยชน์พอสมควร
เยี่ยมเลย ผมเองก็เคยเขียน เว็บเซิร์ฟเวอร์ภาษา C ขึ้นมา และมีซอร์สอยู่ด้านล่าง เคยใช้รันเว็บไซต์เชิงพาณิชย์อยู่พักหนึ่งด้วย
น่าทึ่งว่าเว็บเซิร์ฟเวอร์ HTTP/1.1 สามารถทำให้เล็กและเบาได้แค่ไหน เว็บไซต์เชิงพาณิชย์นั้นรันบนเครื่องที่มี RAM 128MB และ CPU 1 ตัว และเป็นระบบแชตบนเว็บแบบโต้ตอบที่เป็นซอฟต์แวร์ปิด ใช้ให้บริการโรงเรียนในสหราชอาณาจักรจำนวนมากเป็นประจำ แต่แน่นอนว่านั่นเป็นเรื่องเมื่อ 20 ปีก่อน ตอนที่อินเทอร์เน็ตยังไม่เป็นศัตรูเท่าทุกวันนี้
ในบทความบอกว่าบอตทำหน้าที่เป็น fuzzer ได้ยอดเยี่ยม แต่ผมก็ยังคิดว่าควรทำ fuzzing จริง ๆ บ้างสักหน่อย
http://git.annexia.org/?p=rws.git;a=tree
สิ่งที่ต้องใช้:
http://git.annexia.org/?p=c2lib.git;a=tree
http://git.annexia.org/?p=pthrlib.git;a=tree
เว็บไซต์ของผม https://blessed.rs รันบน VM ที่เล็กที่สุดเท่าที่หาได้ คือ RAM 256MB แต่โดยปกติใช้เพียงประมาณ 60MB
สิ่งที่น่าทึ่งจริง ๆ คือเมื่อ memory map มีแค่หน้า 4KB จำนวน 5 หน้า fork ของ Linux จะเร็วมาก
เป็นโปรเจกต์เล็ก ๆ ที่เริ่มทำเล่นในเวลาว่าง คิดว่าที่นี่น่าจะชอบ :)
ยอดเยี่ยมมาก ผมคิดมานานแล้ว และตอนนี้ก็ยังคิดแบบนั้นอยู่ ว่าการใช้ system API ระดับต่ำสุดแบบนี้เพื่อรันบริการขั้นต่ำขึ้นมามันน่าพอใจจริง ๆ
แทบเหมือนเวทมนตร์ และยิ่งเป็นอย่างนั้นเมื่อเห็นมันจัดการทราฟฟิกจริงได้ ผมแปลกใจนิดหน่อยที่
poll()ธรรมดา ๆ ทำตัวเลขได้ถึงขนาดนั้น แต่คงเป็นเพราะผมห่างจากการจัดการอีเวนต์หรือ benchmark ในระดับนั้นมานานแล้วชอบฟังก์ชันต่อการเชื่อมต่อหนึ่งรายการ รวมถึง struct ที่เกี่ยวข้อง วิธีจัดการการเชื่อมต่อด้วย array และ array ของ file descriptor สำหรับ
pollด้วย ทำให้นึกถึงแนวทางที่แพ็กเกจโอเพนซอร์สหลายตัวซึ่งขึ้นชื่อเรื่อง throughput สูงอย่าง nginx, Redis, memcached ใช้กันมันทำให้ผมคิดว่าทุกคนควรรู้และลองใช้ภาษาให้มากที่สุดเท่าที่ทำได้ ไม่ว่าจะเป็นภาษาโปรแกรมหรือภาษาธรรมชาติ การได้ คิดด้วย ภาษาใดภาษาหนึ่งเป็นประสบการณ์ที่ไม่เหมือนใคร บริบทที่ต่างกันทำให้ทุกอย่างรู้สึกต่างออกไป และแม้สุดท้ายจะทำสิ่งคล้ายกัน มุมมองก็เปลี่ยนไป
ตัวอย่างเช่น ถ้าจะเข้าใจแก่นแท้ของ Linux หรือ Git จริง ๆ ก็ต้องพูดภาษานั้นได้ และเข้าใจนัยละเอียดอ่อนที่มักหายไปในการแปล คล้ายกับการจะเข้าใจความหมายเชิงอัตวิสัยที่แท้จริงของคำว่า “ป่า” ในภาษารัสเซีย ก็ต้องพูดและเข้าใจภาษารัสเซีย
บริบทเปลี่ยนมุมมอง และบางครั้งก็เปลี่ยนทุกอย่าง
ผมก็แปลกใจเหมือนกันที่
poll()ธรรมดา ๆ รับไหวขนาดนั้น เคยคิดว่าสักวันคงต้องย้ายไปepollแต่poll()ก็ทำงานได้ดีมากอยู่ไม่มีอะไรใหม่ใต้ดวงอาทิตย์
อันนี้ก็น่าสนใจเช่นกัน: https://news.ycombinator.com/item?id=27431910
ณ ปี 2024 อินสแตนซ์ althttpd ของ sqlite.org จัดการคำขอ HTTP มากกว่า 500,000 ครั้งต่อวัน หรือประมาณ 5–6 ครั้งต่อวินาที และให้บริการคอนเทนต์ราว 200GB ต่อวัน หรือประมาณ 18Mbit/s บน Linode เดือนละ 40 ดอลลาร์ ค่า load average ของเครื่องนี้มักอยู่แถว 0.5 ประมาณ 19% ของคำขอ HTTP เป็น CGI ที่ไปยัง repository ซอร์สโค้ด Fossil หลายตัว
ถ้าอยากเขียนแอป C แต่ไม่สบายใจที่จะเขียนส่วนที่สัมผัสอินเทอร์เน็ตสาธารณะโดยตรงเอง Kore เป็นเฟรมเวิร์กที่ใช้ได้ดี
มีฟีเจอร์ในตัวที่สะดวก เช่น การจัดการใบรับรอง ACME, Pgsql, curl, WebSocket
โดยพื้นฐานคือ build โมดูลแล้วรันและประกอบใช้งานร่วมกันได้ และยังผสม Lua/Python กับ C ได้ด้วย
https://kore.io/
ในที่สุดก็มีเว็บไซต์ที่ขึ้นหน้าแรกแล้วไม่ตาย
ไม่ได้หมายความว่าโปรเจกต์นี้ไม่ยอดเยี่ยม แต่ถ้าในสภาพแวดล้อม production คุณให้ความสำคัญกับเรื่องนี้จริง ๆ และส่วนใหญ่เสิร์ฟคอนเทนต์แบบ static ก็ใช้ CDN ไปเถอะ มันจะให้ประสิทธิภาพดีกว่าแทบทุกอย่างที่คุณเขียนเองเสมอ แค่ไม่สนุกเท่านั้น
โปรเจกต์ดูสะอาดและเจ๋งดี แต่ผมคิดว่าคนส่วนใหญ่คงไปที่ GitHub มากกว่าจะไปลิงก์ที่แสดงหน้าเว็บ ผมพลาดอะไรไปหรือเปล่า?
ชอบตรงที่ว่า “ผมสนุกกับการทำเครื่องมือของตัวเอง และก็เบื่อนิดหน่อยกับการได้ยินว่าทุกอย่างต้อง ผ่านการพิสูจน์ในสนามจริง แล้วถ้ามันตายแล้วไงล่ะ? บั๊กก็แก้ได้ :^)”
HTTP และ HTTPS server แบบครบถ้วนด้วยโค้ด C แค่ 3.4k บรรทัด เองเหรอ? พูดตรง ๆ ผมนึกว่าถ้าจะให้สอดคล้องกับสเปกอย่างครบถ้วนต้องใช้มากกว่านี้เยอะ
แค่รับคำขอ GET และตั้ง
Content-Lengthใน response ก็เพียงพอสำหรับ user agent 99% แล้ว การจัดการTransfer-Encodingกับ header ช่วงไบต์ก็ไม่ได้ทำให้โค้ดเพิ่มขึ้นมากนักHTTPS ก็เป็นแค่ HTTP บน TLS socket และถ้าไม่ได้ implement การเข้ารหัสเอง ระดับ abstraction ก็ควรจะมีเท่านั้น สนุกดี และจริง ๆ ก็ไม่ได้แย่ขนาดนั้น
httpd(8)[1] ของ OpenBSD ตอนนี้ก็ยังไม่ถึง 15,000 บรรทัดนิด ๆ รวมเอกสารแล้วถ้าตัดฟีเจอร์บางอย่างออกและตั้งสมมติฐานไว้บ้าง ก็ไม่น่าแปลกใจถ้าโปรเจกต์นี้จะอยู่ในช่วง 5,000 บรรทัดได้
ผล
$ wc -l *รวมทั้งหมดคือ 14815 บรรทัด[1]: https://man.openbsd.org/httpd.8
แน่นอนว่าผมคงไม่เอาไปเปิดบนอินเทอร์เน็ตสาธารณะ และ implement แค่ส่วนเล็กมากของ HTTP/1.1 แต่มันใช้งานได้ และต้องใช้
mallocเฉพาะตอน initialize เท่านั้นทำให้นึกถึงการบรรยายใน Chaos Communication Congress เกี่ยวกับบล็อก/เว็บเซิร์ฟเวอร์ที่เขียนด้วย C
เนื้อหาใส่ฟีเจอร์ด้านความปลอดภัยไว้มากมาย เช่น immutable storage, การลดสิทธิ์, การไม่ให้บล็อกเข้าถึงใบรับรอง TLS เป็นต้น: https://www.youtube.com/watch?v=TaE28fJVPTk