کتابخانه‌ی استاندارد · شبکه

سرور وب net/httpd

سرور وب با پرونده‌ی پیکربندی: چند سایت روی یک درگاه، مسیرها، هدایت‌ها، حافظه‌ی نهان و پرونده‌های ایستا.

واردسازی net.httpd
در این صفحه
  1. تابع‌ها

این بسته چند وب‌سایت را از یک فرایند و یک مجموعه درگاه اجرا می‌کند؛ کاری که معمولاً به nginx جلوی برنامه‌ها سپرده می‌شود. سایت‌ها را در یک پرونده‌ی پیکربندی توصیف می‌کنید، آن را بار می‌کنید و Run را صدا می‌زنید. فرمان salam serve --config=web.salam دقیقاً همین کار را می‌کند و --test پرونده را بدون راه‌اندازی چیزی بررسی می‌کند.

پیکربندی به همان قالب بلوکی بقیه‌ی سلام نوشته می‌شود: یک تنظیم name = value است، بلوک با name: باز و با end بسته می‌شود، توضیح‌ها با // شروع می‌شوند و هر تنظیم و مقدارش در یک خط می‌مانند. رشته‌ها میان دو نقل‌قول‌اند، یا میان پشت‌نقل‌قول وقتی بک‌اسلش دارند، مثل عبارت باقاعده. کلیدهای پیکربندی انگلیسی‌اند، درست مثل کلیدهای JSON.

server:
    log: access = "/var/log/salam/access.log" end
    site:
        listen = 80
        names = "example.com www.example.com"
        root = "/srv/example/public"
        index = "index.html"
        error page = 404 "404.html"
        location "/assets/": cache = 30d end
        location "= /health": return = 200 "ok" end
        location "/old/": redirect = "https://example.com$uri" code = 301 end
    end
    site:
        names = "blog.example.com *.blog.example.com"
        root = "/srv/blog"
    end
end

دو سایت روی یک درگاه با سرآیند Host از هم جدا می‌شوند. نام دقیق بر نام عام *.example.com می‌چربد و درخواست برای نامی که هیچ‌کس ننوشته به سایتی می‌رسد که default = true دارد، وگرنه به اولین سایت آن درگاه. سایتی که listen ندارد روی درگاه ۸۰ گوش می‌دهد؛ با listen = "127.0.0.1:8080" نشانی را هم تعیین می‌کنید.

داخل یک سایت، مسیر درخواست مثل nginx با بلوک‌های location مقایسه می‌شود:

  • location "= /health": دقیقاً همین مسیر، و چیز دیگری امتحان نمی‌شود.
  • location "/assets/": هر مسیری که با آن شروع شود؛ بلندترین پیشوند به خاطر سپرده می‌شود.
  • location "^~ /static/": پیشوندی که جلوی امتحان عبارت‌های باقاعده را هم می‌گیرد.
  • location "~ \.php$" و "~* \.png$": عبارت باقاعده (دومی به بزرگی و کوچکی حروف توجه نمی‌کند)، بعد از پیشوندها و به ترتیب پرونده امتحان می‌شود؛ اولین مطابقت برنده است، وگرنه بلندترین پیشوند به کار می‌رود.

تنظیم‌های یک سایت، و یک مسیر مگر جایی که نوشته شده:

تنظیممعنا
listen = 80 (سایت)درگاه، به‌دلخواه همراه نشانی: listen = "0.0.0.0:8080". می‌شود آن را تکرار کرد.
names = "a.com b.com" (سایت)نام میزبان‌های سایت؛ *.a.com هر زیردامنه را می‌گیرد.
default = true (سایت)این سایت به نام‌هایی پاسخ می‌دهد که هیچ سایتی ادعا نکرده.
root = "/srv/site"پوشه‌ای که مسیر درخواست به آن چسبانده می‌شود. مسیری که root خودش را دارد جای root سایت را می‌گیرد.
alias = "/srv/files" (مسیر پیشوندی)پوشه‌ای که جای پیشوند مطابق را می‌گیرد: با location "/dl/": alias = "/srv/files" end مسیر /dl/a.zip می‌شود /srv/files/a.zip.
index = "index.html index.htm"پرونده‌هایی که برای یک پوشه به ترتیب امتحان می‌شوند. پیش‌فرض index.html است.
return = 200 "ok"بی‌درنگ با یک وضعیت و متن اختیاری پاسخ می‌دهد.
redirect = "https://$host$request_uri"، code = 301هدایت می‌کند. کد پیش‌فرض ۳۰۱ است. متغیرها: $host، $uri، $args، $request_uri و $scheme. در سطح سایت برای همه‌ی درخواست‌ها و پیش از هر مسیر اجرا می‌شود.
cache = 30dهمراه پرونده‌ها Cache-Control: public, max-age=... می‌فرستد. یکاها: s، m، h، d، w، y.
header = "Name" "value"یک سرآیند پاسخ اضافه می‌کند. سرآیندهای سایت روی همه‌ی پاسخ‌ها می‌روند و سرآیندهای مسیر فقط روی پاسخ‌های همان مسیر.
error page = 404 "404.html"پرونده‌ای نسبت به root که با همان وضعیت خطا فرستاده می‌شود.

بلوک log: مقدار access = "path" می‌گیرد؛ مسیر می‌تواند "stdout" یا "stderr" هم باشد. هر خط زمان، میزبان، روش، مسیر و وضعیت را دارد.

چیزهایی که مردم اشتباه می‌کنند:

  • پرونده‌ها فقط از root سایت مطابق خوانده می‌شوند. مسیر اول پاک‌سازی می‌شود، پس .. نمی‌تواند از آن بالا برود، و هر پرونده یا پوشه‌ای که نامش با نقطه شروع شود، مثل .env یا .git، با ۴۰۴ پاسخ می‌گیرد. .well-known مجاز است.
  • هر پرونده با سرآیندهای ETag و Last-Modified فرستاده می‌شود. درخواستی با If-None-Match مطابق یا If-Modified-Since به‌اندازه‌ی کافی تازه ۳۰۴ بدون بدنه می‌گیرد، و یک Range: bytes=a-b (و همچنین a- و -n) با ۲۰۶ همان بخش را می‌گیرد؛ بازه‌ی بیرون از پرونده ۴۱۶ می‌گیرد. If-Range با ETag رعایت می‌شود. چند بازه در یک درخواست با کل پرونده پاسخ داده می‌شود.
  • برای پرونده‌ها فقط GET و HEAD پاسخ داده می‌شود؛ هر چیز دیگر ۴۰۵ با سرآیند Allow می‌گیرد.
  • نام‌ها و مقدارها هنگام بارکردن پرونده بررسی می‌شوند: تنظیم غلط‌نوشته، درگاه خارج از بازه، پوشه‌ی ریشه‌ی ناموجود، عبارت باقاعده‌ی نادرست یا دو سایت با یک نام روی یک درگاه با خط و ستونش گزارش می‌شود و نام نزدیک پیشنهاد می‌شود. از پرونده‌ای که مشکل دارد چیزی راه نمی‌افتد.
  • listen = 443 tls خوانده می‌شود اما فعلاً رد می‌شود؛ HTTPS در نسخه‌ای بعدی می‌آید، تا آن زمان یک پایان‌دهنده‌ی TLS جلوی آن بگذارید.
  • بسته برای هر فرایند یک پیکربندی بارشده نگه می‌دارد. بار دوم آن را عوض می‌کند، اما سروری که در حال اجراست همان سایت‌هایی را دارد که با آن شروع شد.
  • همه‌ی چیزهایی که ممکن است در پرونده غلط باشد با Check یا LoadText پیدا می‌شود، نه در اولین درخواست.

بسته‌های مرتبط: net/http سرور زیرین است، net/router مسیرها را نگه می‌دارد و net/httpd/conf تجزیه‌گری است که پرونده را می‌خواند.

تابع‌ها

تجزیه مدت ParseDuration

تابع تجزیه مدت(text: رشته): صحیح۶۴
مدتی مثل 45، 10m یا 30d را می‌خواند و به ثانیه برمی‌گرداند، یا ‎-۱ اگر معتبر نباشد. یکاها s، m، h، d، w و y (۳۶۵ روز) هستند؛ بدون یکا یعنی ثانیه.
net-httpd-ParseDuration.salam
واردسازی شبکه.سرور وب

تابع ریشه:
    سرچاپ سرور وب.تجزیه مدت("45")
    سرچاپ سرور وب.تجزیه مدت("90s")
    سرچاپ سرور وب.تجزیه مدت("10m")
    سرچاپ سرور وب.تجزیه مدت("12h")
    سرچاپ سرور وب.تجزیه مدت("30d")
    سرچاپ سرور وب.تجزیه مدت("2w")
    سرچاپ سرور وب.تجزیه مدت("1y")
    سرچاپ سرور وب.تجزیه مدت("")
    سرچاپ سرور وب.تجزیه مدت("abc")
    سرچاپ سرور وب.تجزیه مدت("5x")
پایان
خروجی45 90 600 43200 2592000 1209600 31536000 -1 -1 -1

بارگذاری متن پیکربندی LoadText

تابع بارگذاری متن پیکربندی(text: رشته، file: رشته): منطقی
پیکربندی را از متن بار می‌کند. آرگومان دوم نام پرونده در پیام مشکل‌هاست. اگر مشکلی نباشد true برمی‌گرداند؛ وگرنه مشکل‌ها برای ProblemAt می‌مانند و چیز دیگری عوض نمی‌شود.
net-httpd-Serve.salam
واردسازی ورودی خروجی
واردسازی سیستم عامل
واردسازی زمان
واردسازی رشته
واردسازی شبکه.اچ تی تی پی
واردسازی شبکه.سرور وب

تابع سرو():
    سرور وب.اجرا()
پایان

تابع بگیر(میزبان: رشته، مسیر: رشته): اچ تی تی پی.پاسخ:
    ج := اچ تی تی پی.انجام درخواست("GET"، "http://" + میزبان + ":19435" + مسیر، اچ تی تی پی.سرصفحه جدید()، ""، اچ تی تی پی.گزینه های درخواست {})
    برگشت ج
پایان

تابع ریشه:
    ریشه_پوشه := سیستم عامل.پوشه موقت() + "/salam_httpd_serve_fa"
    سیستم عامل.حذف همه(ریشه_پوشه)
    سیستم عامل.ساخت پوشه ها(ریشه_پوشه + "/shop")
    سیستم عامل.ساخت پوشه ها(ریشه_پوشه + "/blog")
    ورودی خروجی.نوشتن پرونده(ریشه_پوشه + "/shop/index.html"، "فروشگاه")
    ورودی خروجی.نوشتن پرونده(ریشه_پوشه + "/blog/index.html"، "وبلاگ")
    ورودی خروجی.نوشتن پرونده(ریشه_پوشه + "/blog/404.html"، "چنین نوشته‌ای نیست")
    متن := `
server:
    site:
        listen = 19435
        names = "localhost"
        root = "@ROOT@/shop"
        header = "X-Site" "shop"
        location "= /health": return = 200 "ok" end
        location "/old/": redirect = "http://$host/new$uri" code = 302 end
    end
    site:
        listen = 19435
        names = "127.0.0.1"
        root = "@ROOT@/blog"
        error page = 404 "404.html"
    end
end
`
    سرچاپ سرور وب.بارگذاری متن پیکربندی(رشته.جایگزینی(متن، "@ROOT@"، ریشه_پوشه)، "serve.salam")
    ن := نخ‌ساز(سرو)
    زمان.خواب(۳۰۰)
    الف := بگیر("localhost"، "/")
    سرچاپ الف.وضعیت، الف.بدنه، اچ تی تی پی.دریافت سرصفحه(الف، "x-site")
    ب := بگیر("127.0.0.1"، "/")
    سرچاپ ب.وضعیت، ب.بدنه
    سرچاپ بگیر("localhost"، "/health").بدنه
    پ := بگیر("localhost"، "/old/page")
    سرچاپ پ.وضعیت، اچ تی تی پی.دریافت سرصفحه(پ، "location")
    ت := بگیر("127.0.0.1"، "/missing")
    سرچاپ ت.وضعیت، ت.بدنه
    سرچاپ بگیر("localhost"، "/.git/config").وضعیت
    سرور وب.توقف()
    نخ‌پیوند(ن)
    سیستم عامل.حذف همه(ریشه_پوشه)
پایان
خروجیtrue 200 فروشگاه shop 200 وبلاگ ok 302 http://localhost:19435/new/old/page 404 چنین نوشته‌ای نیست 404

سایت برای SiteFor

تابع سایت برای(host: رشته، port: صحیح): صحیح
شماره‌ی سایتی که روی یک درگاه به یک نام میزبان پاسخ می‌دهد، یا ‎-۱ اگر هیچ سایتی نباشد. نام به حروف کوچک تبدیل می‌شود و درگاه یا نقطه‌ی پایانی نادیده گرفته می‌شود. نام دقیق بر نام عام می‌چربد و نامی که کسی ننوشته به سایت پیش‌فرض آن درگاه می‌رسد.
net-httpd-Match.salam
واردسازی شبکه.سرور وب

تابع ریشه:
    متن := `
server:
    site:
        listen = 8080
        names = "example.test *.example.test"
        location "/assets/": cache = 30d end
        location "= /health": return = 200 "ok" end
        location "^~ /static/": cache = 1h end
        location "~ \.php$": return = 403 "no" end
    end
    site:
        listen = 8080
        names = "other.test"
        default = true
    end
end
`
    سرچاپ سرور وب.بارگذاری متن پیکربندی(متن، "match.salam")، سرور وب.شمار مشکل ها()
    سرچاپ سرور وب.شمار سایت ها()، سرور وب.نام های سایت(۰)، سرور وب.نام های سایت(۱)
    سرچاپ سرور وب.سایت برای("example.test"، ۸۰۸۰)، سرور وب.سایت برای("API.Example.Test:8080"، ۸۰۸۰)، سرور وب.سایت برای("other.test."، ۸۰۸۰)
    سرچاپ سرور وب.سایت برای("unknown.test"، ۸۰۸۰)، سرور وب.سایت برای("example.test"، ۹۰۹۰)
    سرچاپ سرور وب.الگوی مسیر(سرور وب.مسیر برای(۰، "/health"))
    سرچاپ سرور وب.الگوی مسیر(سرور وب.مسیر برای(۰، "/assets/logo.png"))
    سرچاپ سرور وب.الگوی مسیر(سرور وب.مسیر برای(۰، "/static/a.php"))
    سرچاپ سرور وب.الگوی مسیر(سرور وب.مسیر برای(۰، "/x/index.php"))
    سرچاپ سرور وب.مسیر برای(۰، "/plain")، سرور وب.مسیر برای(۱، "/anything")
پایان
خروجیtrue 0 2 example.test *.example.test other.test 0 0 1 1 -1 /health /assets/ /static/ \.php$ -1 -1

مسیر برای LocationFor

تابع مسیر برای(site: صحیح، path: رشته): صحیح
شماره‌ی مسیری که یک مسیر درخواست را در یک سایت می‌گیرد، یا ‎-۱ وقتی فقط تنظیم‌های سایت اجرا می‌شود. مسیر دقیق می‌چربد، بعد پیشوند ^~، بعد اولین عبارت باقاعده‌ی مطابق و بعد بلندترین پیشوند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: net-httpd-Match.salam

شمار سایت ها SiteCount

تابع شمار سایت ها(): صحیح
شمار سایت‌های پیکربندی بارشده.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: net-httpd-Match.salam

نام های سایت SiteNames

تابع نام های سایت(site: صحیح): رشته
نام میزبان‌های یک سایت، با فاصله از هم جدا شده.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: net-httpd-Match.salam

الگوی مسیر LocationPattern

تابع الگوی مسیر(loc: صحیح): رشته
الگوی یک مسیر بدون نشانه‌ی =، ^~ یا ~.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: net-httpd-Match.salam

شمار مشکل ها ProblemCount

تابع شمار مشکل ها(): صحیح
شمار مشکل‌های پیداشده هنگام بارکردن آخرین پیکربندی؛ صفر یعنی معتبر است.
net-httpd-Problems.salam
واردسازی شبکه.سرور وب

تابع آزمون(متن: رشته):
    درست‌بود := سرور وب.بارگذاری متن پیکربندی(متن، "bad.salam")
    سرچاپ درست‌بود، سرور وب.شمار مشکل ها()
    تکرار سرور وب.شمار مشکل ها() در ای:
        سرچاپ سرور وب.مشکل شماره(ای)
    پایان
پایان

تابع ریشه:
    آزمون("server:\n    sit:\n    end\nend\n")
    آزمون("server:\n    site:\n        listen = 70000\n    end\nend\n")
    آزمون("server:\n    site:\n        names = \"a.test\"\n        root = \"/no/such/dir\"\n    end\nend\n")
    آزمون("server:\n    site:\n        names = \"a.test\"\n    end\n    site:\n        names = \"a.test\"\n    end\nend\n")
    آزمون("server:\n    site:\n        listen = 443 tls\n    end\nend\n")
    آزمون("server:\n    site:\n        names = \"a.test\"\n        location \"/x\": hedaer = \"A\" \"b\" end\n    end\nend\n")
    آزمون("server:\nend\n")
    آزمون("server:\n    site:\n        names = \"a.test\"\n        cache = soon\n    end\nend\n")
    آزمون("server:\n    site:\n        names = \"a.test\"\n        location \"/x\": alias = \"/tmp\" end\n        location \"~ x\": alias = \"/tmp\" end\n    end\nend\n")
پایان
خروجیfalse 1 bad.salam:2:5: unknown block 'sit' (did you mean 'site'?) false 1 bad.salam:3:18: port must be a number from 1 to 65535 false 1 bad.salam:2:5: root '/no/such/dir' is not a directory false 1 bad.salam:5:5: name 'a.test' is used by two sites on port 80 false 1 bad.salam:3:22: tls is not available yet false 1 bad.salam:4:24: unknown setting 'hedaer' in a location (did you mean 'header'?) false 1 bad.salam:1:1: the config has no sites false 1 bad.salam:4:9: cache must be a duration such as 30s, 10m, 12h, 30d or 1y false 1 bad.salam:5:9: alias works only with a prefix location, use root for the others

مشکل شماره ProblemAt

تابع مشکل شماره(i: صحیح): رشته
یک مشکل به شکل file:line:column: message، با شماره‌گذاری از ۰.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: net-httpd-Problems.salam

بارگذاری پرونده LoadFile

تابع بارگذاری پرونده(path: رشته): منطقی
پرونده را می‌خواند و مثل LoadText بار می‌کند. پرونده‌ای که خوانده نشود هم یک مشکل است.
net-httpd-Run.salam
واردسازی سیستم عامل
واردسازی شبکه.سرور وب

تابع ریشه:
    ناپایا مسیر := "web.salam"
    آرگ := سیستم عامل.آرگومان ها()
    اگر آرگ.طول() > ۱:
        مسیر = آرگ[۱]
    پایان
    اگر سرور وب.بررسی(مسیر) != ۰:
        برگشت ۱
    پایان
    اگر وارونه سرور وب.بارگذاری پرونده(مسیر):
        برگشت ۲
    پایان
    کد := سرور وب.اجرا()
    سرور وب.توقف()
    برگشت کد
پایان

بررسی Check

تابع بررسی(path: رشته): صحیح
پرونده را همان‌طور بار می‌کند که salam serve --config=FILE --test می‌کند: مشکل‌ها را در خروجی خطا چاپ می‌کند و ۱ برمی‌گرداند، یا یک خط خلاصه چاپ می‌کند و ۰ برمی‌گرداند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: net-httpd-Run.salam

اجرا Run

تابع اجرا(): صحیح
برای هر نشانی متمایز در پیکربندی بارشده یک شنونده راه می‌اندازد و منتظر می‌ماند تا Stop صدا زده شود. اگر پیکربندی معتبری بار نشده باشد، گزارش باز نشود یا نشانی بسته نشود، بی‌درنگ ۲ برمی‌گرداند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: net-httpd-Serve.salam

توقف Stop

تابع توقف()
باعث می‌شود Run برگردد. اتصال‌های پذیرفته‌شده تخلیه نمی‌شوند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: net-httpd-Serve.salam