檢視原始碼 HTTP 伺服器

組態設定

HTTP 伺服器,也稱為 httpd,按照 RFC 2616 中描述的方式處理 HTTP 請求,但有一些例外,例如閘道和代理功能。只要底層機制也支援,伺服器就支援 IPv6。

此伺服器實作了許多功能,例如:

  • 安全通訊端層 (SSL)
  • Erlang 腳本介面 (ESI)
  • 通用閘道介面 (CGI)
  • 使用者驗證 (使用 Mnesia、Dets 或純文字資料庫)
  • 通用日誌檔格式 (支援或不支援 disk_log(3))
  • URL 別名
  • 動作對應
  • 目錄列表

伺服器的組態設定以 Erlang 屬性列表的形式提供。

Inets 5.0 版本開始,HTTP 伺服器是一個易於啟動/停止和自訂的 Web 伺服器,提供最基本的 Web 伺服器功能。Inets 專為嵌入式系統設計,如果您需要功能完善的 Web 伺服器,還有其他 Erlang 開源替代方案。

幾乎所有伺服器功能都是使用特別設計的伺服器 API 實作的,該 API 在 Erlang Web 伺服器 API 中說明。此 API 可用於增強核心伺服器功能,例如自訂記錄和驗證。

以下內容應放入 Erlang 節點應用程式組態檔中,以在應用程式啟動時啟動 HTTP 伺服器:

[{inets, [{services, [{httpd, [{proplist_file,
           "/var/tmp/server_root/conf/8888_props.conf"}]},
          {httpd, [{proplist_file,
           "/var/tmp/server_root/conf/8080_props.conf"}]}]}]}].

伺服器使用 Erlang 屬性列表進行組態設定。有關可用的屬性,請參閱 httpd

可用的組態屬性如下:

httpd_service() -> {httpd, httpd()}
httpd()         -> [httpd_config()]
httpd_config()  -> {proplist_file, file()}
                   {debug, debug()} |
                   {accept_timeout, integer()}
debug()         -> disable | [debug_options()]
debug_options() -> {all_functions, modules()} |
                   {exported_functions, modules()} |
                   {disable, modules()}
modules()       -> [atom()]

此處:

  • {proplist_file, file()} - 包含 Erlang 屬性列表的檔案,後面接著句點,描述 HTTP 伺服器組態設定。

  • {debug, debug()} - 可以啟用所有函數的追蹤,或者僅啟用所選模組的匯出函數的追蹤。

  • {accept_timeout, integer()} - 設定伺服器建立請求連線所需的逾時值。

入門

啟動 Inets

1> inets:start().
ok

使用最少必要的組態設定啟動 HTTP 伺服器。如果指定埠 0,則會使用任意可用的埠,您可以使用函數 info 來找出選擇的埠號。

2> {ok, Pid} = inets:start(httpd, [{port, 0}, {server_root,"/tmp"},
.. {document_root,"/tmp/htdocs"}, {bind_address, "localhost"}]).
{ok, 0.79.0}

呼叫 info

3> httpd:info(Pid).
[{mime_types,[{"html","text/html"},{"htm","text/html"}]},
 {server_name,"machine.local"},
 {bind_address, {127,0,0,1}},
 {server_root,"/tmp"},
 {port,59408},
 {document_root,"/tmp/htdocs"},
 {ipfamily,inet}]

重新載入組態設定,而無需重新啟動伺服器

4> httpd:reload_config([{port, 59408},
.. {server_root,"/tmp/www_test"}, {document_root,"/tmp/www_test/htdocs"},
.. {bind_address, "localhost"}], non_disturbing).
ok.

注意

portbind_address 無法變更。在重新載入期間嘗試存取伺服器的用戶端會收到服務暫時不可用的回覆。

5> httpd:info(Pid, [server_root, document_root]).
[{server_root,"/tmp/www_test"},{document_root,"/tmp/www_test/htdocs"}]
6> ok = inets:stop(httpd, Pid).

替代方案

6> ok = inets:stop(httpd, {{127,0,0,1}, 59408}).

請注意,bind_address 必須是函數 info 回報的 IP 位址,而不是放入 bind_address 時允許的主機名稱。

動態網頁

Inets HTTP 伺服器提供兩種建立動態網頁的方式,每種方式都有其優缺點:

  • CGI 腳本 - 通用閘道介面 (CGI) 腳本可以使用任何程式語言編寫。CGI 腳本已標準化,並受大多數 Web 伺服器支援。CGI 腳本的缺點是,由於其設計,它們會消耗大量資源。CGI 要求伺服器為它需要啟動的每個可執行檔分叉一個新的 OS 處理程序。

  • ESI 函數 - Erlang 伺服器介面 (ESI) 函數提供了一個緊密且有效率的 Erlang 函數執行介面。另一方面,此介面是 Inets 特有的。

CGI 版本 1.1,RFC 3875

模組 mod_cgi 允許在伺服器上執行 CGI 腳本。符合 ScriptAlias 組態指令定義的檔案會被視為 CGI 腳本。CGI 腳本由伺服器執行,其輸出會傳回給用戶端。

CGI 腳本回應包含訊息標頭和訊息主體,兩者之間以空行分隔。訊息標頭包含一個或多個標頭欄位。主體可以是空的。

範例

"Content-Type:text/plain\nAccept-Ranges:none\n\nsome very
	plain text"

伺服器會解譯訊息標頭,並且大部分標頭會轉換為 HTTP 標頭,並與訊息主體一起傳回給用戶端。

CGI-1.1 的支援是按照 RFC 3875 實作的。

ESI

Erlang 伺服器介面由模組 mod_esi 實作。

ERL 方案

erl 方案旨在模擬純 CGI,但沒有額外的負擔。呼叫 Erlang erl 函數的 URL 具有以下語法 (正規表示式):

http://your.server.org/***/Module[:/]Function(?QueryString|/PathInfo)

*** 取決於 ErlScriptAlias 組態指令的使用方式。

參考的模組 Module 必須在程式碼路徑中找到,並且必須定義一個具有兩個或三個參數的函數 Function。最好實作一個具有三個參數的函數,因為它允許在產生階段將網頁的區塊傳送給用戶端,而不是先產生整個網頁,然後再將其傳送給用戶端。保留實作具有兩個參數的函數的選項僅是為了向後相容性。有關 ESI 回呼函數的實作詳細資訊,請參閱 mod_esi

記錄

支援三種類型的記錄:傳輸記錄、安全記錄和錯誤記錄。事實上的標準通用日誌檔格式用於傳輸和安全記錄。有許多統計程式可用於分析通用日誌檔格式。通用日誌檔格式如下所示:

remotehost rfc931 authuser [date] "request" status bytes

此處:

  • remotehost - 遠端主機名稱。

  • rfc931 - 用戶端遠端使用者名稱 (RFC 931)。

  • authuser - 用於驗證的使用者名稱。

  • [date] - 請求的日期和時間 (RFC 1123)。

  • "request" - 請求行與來自用戶端的請求行完全相同 (RFC 1945)。

  • status - 傳回給用戶端的 HTTP 狀態碼 (RFC 1945)。

  • bytes - 傳輸的文件內容長度。

內部伺服器錯誤記錄在錯誤記錄檔中。此檔案的格式比使用通用日誌檔格式的記錄格式更不計劃,但符合以下語法:

[date] 存取 path 失敗,適用於 remotehost,原因:reason

Erlang Web 伺服器 API

處理 HTTP 請求的過程涉及幾個步驟,例如:

  • 建立連線、傳送和接收資料。
  • URI 到檔案名稱轉換。
  • 驗證/存取檢查。
  • 擷取/產生回應。
  • 記錄。

為了提供 HTTP 伺服器請求處理的自訂和擴充性,這些步驟中的大多數由一個或多個模組處理。這些模組可以在執行階段更換或移除,並且可以新增新的模組。對於每個請求,所有模組都按照伺服器組態檔中模組指令指定的順序進行遍歷。某些部分(主要是與通訊相關的步驟)被視為伺服器核心功能,並且不是使用 Erlang Web 伺服器 API 實作的。Erlang Web 伺服器 API 實作的功能說明在 Inets Web 伺服器模組區段中說明。

模組可以使用 Erlang Web 伺服器 API 模組序列中先前模組產生的資料,或產生要由連續 Erlang Web 伺服器 API 模組使用的資料。這是因為存在內部鍵值組列表,稱為互動資料。

注意

互動資料會強制執行模組相依性,並且應盡可能避免。這表示模組屬性中模組的順序很重要。

API 說明

使用 Erlang Web 伺服器 API 實作伺服器功能的每個模組都必須實作以下回呼函數:

  • do/1(強制) - 要處理請求時呼叫的函數
  • load/2
  • store/2
  • remove/1

只有在需要引入新的組態指令時,才需要後面的函數。有關詳細資訊,請參閱 httpd

Inets Web 伺服器模組

慣例是,實作某些 Web 伺服器功能的所有模組都具有名稱 mod_*。組態設定 Web 伺服器時,這些模組的適當選擇必須存在於模組指令中。請注意,有一些互動相依性需要考慮,因此模組的順序不能隨機。

mod_action - 基於檔案類型/方法的腳本執行

每當請求特定類型或 HTTP 方法 (請參閱 RFC 1945) 的檔案時,此模組都會執行 CGI 腳本。

使用以下 Erlang Web 伺服器 API 互動資料:

如果可能,匯出以下 Erlang Web 伺服器 API 互動資料:

  • {new_request_uri, RequestURI} - 已產生替代 RequestURI

mod_alias - URL 別名

mod_alias 模組可以將主機檔案系統的不同部分映射到文件樹中,也就是建立別名和重新導向。

如果可能,匯出以下 Erlang Web 伺服器 API 互動資料:

  • {real_name, PathData} - PathData 是用於 API 函式 mod_alias:path/3 的參數。

mod_auth - 使用者身份驗證

mod_auth 模組提供了使用文字檔案、Dets 資料庫以及 Mnesia 資料庫進行基本使用者身份驗證的功能。

使用以下 Erlang Web 伺服器 API 互動資料:

匯出以下 Erlang Web 伺服器 API 互動資料

  • {remote_user, User} - 用於身份驗證的使用者名稱。

Mnesia 作為身份驗證資料庫

如果使用 Mnesia 作為儲存方法,則必須在 HTTP 伺服器啟動之前啟動 Mnesia。第一次啟動 Mnesia 時,必須先建立 schema 和表格,然後才能啟動 Mnesia。此處提供一個包含兩個函式的簡單模組範例,該模組用於建立和啟動 Mnesia。函式 first_start/0 用於第一次啟動。它會建立 schema 和表格。start/0 用於後續啟動。start/0 會啟動 Mnesia 並等待表格初始化。只有在 schema 和表格已建立後才能使用此函式。

-module(mnesia_test).
-export([start/0,load_data/0]).
-include_lib("mod_auth.hrl").

first_start() ->
    mnesia:create_schema([node()]),
    mnesia:start(),
    mnesia:create_table(httpd_user,
                        [{type, bag},
                         {disc_copies, [node()]},
                         {attributes, record_info(fields,
                                                  httpd_user)}]),
    mnesia:create_table(httpd_group,
                        [{type, bag},
                         {disc_copies, [node()]},
                         {attributes, record_info(fields,
                                                  httpd_group)}]),
    mnesia:wait_for_tables([httpd_user, httpd_group], 60000).

start() ->
    mnesia:start(),
    mnesia:wait_for_tables([httpd_user, httpd_group], 60000).

為了建立 Mnesia 表格,我們使用在 mod_auth.hrl 中定義的兩個記錄,因此必須包含該檔案。first_start/0 會建立一個 schema,指定資料庫要駐留在哪些節點上。然後它會啟動 Mnesia 並建立表格。第一個參數是表格的名稱,第二個參數是如何建立表格的選項清單,詳情請參閱 mnesia 文件。由於 mod_auth_mnesia 的實作會為每個使用者儲存一個列,因此類型必須是 bag。當 schema 和表格建立完成後,會使用函式 mnesia:start/0 來啟動 Mnesia 並等待表格載入。如果啟動時指定了 mnesia_dir,Mnesia 會使用該目錄,否則 Mnesia 會使用目前目錄。基於安全性考量,請確保 Mnesia 表格儲存在 HTTP 伺服器的文件樹之外。如果將它們放置在它保護的目錄中,用戶端可以下載這些表格。只有 Dets 和 Mnesia 儲存方法允許將動態使用者資料寫入磁碟。plain 是一種唯讀方法。

mod_cgi - CGI 腳本

此模組處理 CGI 腳本的調用。

mod_dir - 目錄

如果用戶端發送對目錄而不是檔案的請求,此模組會產生 HTML 目錄列表 (Apache 樣式)。如果不需要目錄列表,則必須從 Modules config 指令中移除此模組。

使用以下 Erlang Web 伺服器 API 互動資料:

匯出以下 Erlang Web 伺服器 API 互動資料

  • {mime_type, MimeType} - 傳入 URL 的檔案後綴映射到 MimeType

mod_disk_log - 使用 Disk_Log 進行記錄

使用 "Common Logfile Format" 和 disk_log 的標準記錄。

使用以下 Erlang Web 伺服器 API 互動資料:

  • remote_user - 來自 mod_auth

mod_esi - Erlang 伺服器介面

mod_esi 模組實作了 Erlang 伺服器介面 (ESI),提供了一個與 Erlang 函式執行緊密且有效率的介面。

使用以下 Erlang Web 伺服器 API 互動資料

  • remote_user - 來自 mod_auth

匯出以下 Erlang Web 伺服器 API 互動資料

  • {mime_type, MimeType} - 傳入 URL 的檔案後綴映射到 MimeType

mod_get - 常規 GET 請求

此模組負責處理對常規檔案的 GET 請求。針對檔案部分內容的 GET 請求由 mod_range 處理。

使用以下 Erlang Web 伺服器 API 互動資料

mod_head - 常規 HEAD 請求

此模組負責處理對常規檔案的 HEAD 請求。對動態內容的 HEAD 請求由負責動態內容的各個模組處理。

使用以下 Erlang Web 伺服器 API 互動資料:

mod_log - 使用文字檔案記錄

使用 "Common Logfile Format" 和文字檔案的標準記錄。

使用以下 Erlang Web 伺服器 API 互動資料:

  • remote_user - 來自 mod_auth

mod_range - 帶有 Range 標頭的請求

此模組回應對檔案一個或多個範圍的請求。這在下載大型檔案時特別有用,因為可以恢復中斷的下載。

請注意,請求文件多個部分的請求會在記錄檔中報告大小為零。

使用以下 Erlang Web 伺服器 API 互動資料:

mod_response_control - 帶有 If* 標頭的請求

此模組會控制請求中的條件是否滿足。例如,請求可以指定只有在內容自上次擷取以來未變更時才對答案感興趣。如果內容已變更,則範圍請求將轉換為對整個檔案的請求。

如果用戶端發送多個限制伺服器回應權限的標頭欄位,則標準未指定應如何處理。 httpd 會按照以下順序控制每個欄位,如果其中一個欄位與目前狀態不符,則會使用正確的回應拒絕請求

If-modified

If-Unmodified

If-Match

If-Nomatch

使用以下 Erlang Web 伺服器 API 互動資料:

匯出以下 Erlang Web 伺服器 API 互動資料

  • {if_range, send_file} - 不符合範圍請求的條件。不得將回應視為範圍請求,而必須視為普通的 get 請求。

mod_security - 安全性篩選器

mod_security 模組充當 mod_auth 中處理的已驗證請求的篩選器。它提供了一種可能性,可以限制使用者在多次驗證失敗後在指定的時間內無法存取。它會記錄驗證失敗以及封鎖使用者,並且會在事件發生時呼叫可設定的回調模組。

還有一個 API 可以手動封鎖或解除封鎖使用者。此 API 還可以列出被封鎖的使用者或在可設定的時間內已驗證的使用者。

mod_trace - TRACE 請求

mod_trace 負責處理 TRACE 請求。Trace 是 HTTP/1.1 中的一種新請求方法。trace 請求的預期用途是進行測試。trace 回應的主體是回應的 Web 伺服器或 Proxy 收到的請求訊息。

從命令列提供檔案

httpd 包含從命令列快速提供檔案的功能。以最簡單的形式,erl -S httpd 會在 localhost 上提供本機目錄中的檔案。

  • --port - 設定要綁定的連接埠。預設為 8000

  • --bind - 設定要接聽的綁定位址。預設為 127.0.0.1

  • DIRECTORY - 設定要從中提供資料的目錄。預設為目前目錄。

例如,要從連接埠 4000 上的目錄 test_results 提供檔案

erl -S httpd serve --port 4000 test_results

如需所有選項的完整參考,請執行 erl -S httpd serve --help