顯示具有 cgi 標籤的文章。 顯示所有文章
顯示具有 cgi 標籤的文章。 顯示所有文章

2025年10月30日 星期四

測試 fastcgi

幫助別人很容易, 請人家來幫助我們卻很困難。
網站技術我只會 cgi, 當然也聽過 fastcgi, 但直到 20251027, 靠著 chatgpt, 才成功使用 c 寫出 fastcgi 程式, 包含架設 lighttpd 大概花了 30 分鐘左右。參考: fastcgi by chatgpt

寫 cgi 免不了要架設 web server, 以前使用 apache2, 設定並不容易, 現在 Nginx 是主流, 但我不熟悉, 不想搞這個, chatgpt 列出了 lighttpd, 就你了,
sudo apt-get install lighttpd spawn-fcgi libfcgi-dev
輕鬆搞定 web server。

lighthttpd root 在 /etc/lighttpd/lighttpd.conf 定義
server.document-root = "/var/www/html"
sudo lighty-enable-mod fastcgi
輕鬆掛入 fastcgi 模組。

再來就是 list 1 fastcgi 的設定, 指定 fastcgi path。

最後就是 c 寫的 fastcgi 程式。

一般比較少用 c 寫 cgi 程式, 但我從接觸 cgi 以來, 就是用 c/c++ 寫, 那時候 perl 是最流行的 cgi 語言, 用 c/c++ 還真是難倒當時的我, 花了很多力氣才搞定。我用 cgicc 來幫忙處理 cgi 的輸入, 簡化許多操作。

把這些都集合起來, 就完成了 fastcgi, 多年來想做的終於完成。

在 debian unstable 上測試, 除了 list 1 的設定檔錯誤之外, 大多沒問題, 照這做就成功了。chatgpt 說明要編輯 15-fastcgi.conf, 但在我的系統上, 是編輯 list 1 這個檔案, 貼上 L5 ~ L17 內容即可。

list 1. /etc/lighttpd/conf-available/10-fastcgi.conf
 1 # /usr/share/doc/lighttpd/fastcgi.txt.gz
 2 # http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ConfigurationOptions#mod_fastcgi-fastcgi
 3
 4 server.modules += ( "mod_fastcgi" )
 5 fastcgi.server = (
 6     "/fcgi" => (
 7         (
 8             "socket" => "/tmp/hello_fcgi.socket",
 9             "bin-path" => "/usr/lib/cgi-bin/hello_fcgi",
10             "check-local" => "disable",
11             "max-procs" => 1,
12             "bin-environment" => (
13                 "LD_LIBRARY_PATH" => "/lib/x86_64-linux-gnu/"
14             )
15         )
16     )
17 )


hello_fcgi.c 是 fastcgi 範例, 使用了 libfcgi, 編譯指令:
gcc hello_fcgi.c -o hello_fcgi -lfcgi
hello_fcgi 放到 /usr/lib/cgi-bin/hello_fcgi
hello_fcgi.c
 1 #include <fcgi_stdio.h>
 2 #include <stdlib.h>
 3 
 4 int main(void) {
 5     int count = 0;
 6     while (FCGI_Accept() >= 0) {
 7         count++;
 8         printf("Content-Type: text/html\r\n\r\n");
 9         printf("<h1>Hello FastCGI</h1>\n");
10         printf("<p>Request #%d</p>\n", count);
11     }
12     return 0;
13 }


最後在瀏覽器貼上 http://localhost/fcgi 即可, /fcgi 對應到 list 1 L6, 實際執行 /usr/lib/cgi-bin/hello_fcgi, 而在 lighttpd 執行起來時, ps 指令也可以看到 hello_fcgi 會被執行起來。

使用 c++ 寫 fastcgi, 發現問題蠻多的, 「只要 #include <string> 或 <iostream> 就發生「500 Internal Server Error」, 「發生 500 Internal Server Error」一般是類似 segmentation fault 之類的錯誤。但程式並沒有發生 segmentation fault。只要在 include fcgi_stdio.h 之前 include string, iostream 就可以避開。另外也不能用 cout, 一樣會「發生 500 Internal Server Error」, 似乎有個 fastcgipp 可以處理, 但我不想嘗試了。用 printf 沒什麼不好。include vector 就沒這問題, 可以放在 fcgi_stdio.h 之後。

d.cpp
 1 #include <string>
 2 #include <iostream>
 3
 4 #include <fcgi_stdio.h>
 5
 6 using namespace std;
 7
 8 int main() {
 9     int count = 0;
10     while (FCGI_Accept() >= 0) {
11         printf("Content-type: text/html\r\n\r\n");
12         printf("<h1>Hello from C++ FastCGI!</h1>\n");
13         #if 0
14         std::cout << "Content-type: text/html\r\n\r\n";
15         std::cout << "<html><body>";
16         std::cout << "<h1>Hello from C++ FastCGI!</h1>";
17         std::cout << "<p>Request #" << ++count << "</p>";
18         std::cout << "</body></html>" << std::endl;
19         #endif
20     }
21     return 0;
22 }

2013年12月2日 星期一

[懷舊] 開發 cgi 程式 (1) 使用一個 form 上傳多個檔案

看到 picasaweb 可以使用一個 form 來選擇多個檔案, 這真是很方便的功能, 我想「知道」這是怎麼做的?

首先當然要來一個 html form 可以讓使用者選擇多個檔案。

mf.html
 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 3 <!
 4 ref: http://stackoverflow.com/questions/3448117/
 5 can-perls-cgi-pm-process-firefoxs-input-type-file-multiple-form-fields
 6 -->
 7 <html>
 8   <head>
 9     <title>Multiple file upload test</title>
10   </head>
11 
12   <body>
13     <form action="http://127.0.01/cgi-bin/cgi1" 
14           method="post" 
15           enctype="multipart/form-data">
16       <input type="file" 
17             name="multiple_files" 
18             multiple="true"/>
19       <button type="submit">Submit</button>
20     </form>
21   </body>
22 </html>

L18 就是可以一次選取多個檔案的關鍵, 這樣在選取檔案時, 就可以依據 shift, ctrl 來選取多個檔案。



從 wiresharek 可以觀察到多個檔案如何被封裝起來, 123\n, zyx\n, 1q2w\n, 是我上傳的 3 個檔案的內容, 透過
boundary= "------- 1316" 

來分割這三個部份, 取出每一段的檔名資訊, 檔案的內容, 依據檔名存檔, 這樣就完成了。說來簡單, 不過程式花了我一番功夫才正確運作。

source code:
https://github.com/descent/cgi_src

ref:
HTTP 檔案上傳機制解析 [精華]
http://stackoverflow.com/questions/3448117/can-perls-cgi-pm-process-firefoxs-input-type-file-multiple-form-fields
http 上傳檔案的容量限制

2013年11月27日 星期三

[懷舊] 開發 cgi 程式 (0)

你一定會說 cgi 落伍了吧, 現在的 web 系統用 cgi 來開發可能不太實際, 目前的 (2013) 網頁程式複雜度已經和十年前有所不同, 我是很認同這些觀念的, 除了落伍這個詞, 我比較喜歡用少用來代替落伍。

CGI 是我唯一學過的 web 技術, 從 cgi, isapi, nsapi, asp, jsp, php, asp.net, python framework, ruby on rails, 有些紅過, 有些已經被淘汰, 有些目前當紅, 未來還有哪些新星, 不知道, 不過 cgi 還是活得好好的就是, 所以就算經過十年了, 我現在還是可以用上 cgi。

以目前的網站生態來說, 用 cgi 開發可能是苦了點, 我也不是鼓勵使用 cgi 來開發目前的網站系統, 畢竟新東西還是有他厲害/方便的地方。

但是在某些資源比較緊的嵌入式系統, 還是有可能會需要用上以 c 來撰寫 cgi 程式, 所以這個老東西還是有發揮的地方。

我們來懷舊一下。

evn: ubuntu 12.04

install apache2
apt-get install apache2

cgi 麻煩的一點是它的設定, apache2 的設定很煩雜, 使用 ubuntu 預設值就好。不過似乎每個 web framework 都需要這耶煩雜的設定, apache2/cgi 的設定算是簡單。

查看系統預設的 cgi 設定值
/etc/apache2/sites-enabled/000-default

從 L16~22 可以得知, cgi 設定, cgi 程式放在 /usr/lib/cgi-bin/, url 則為: http://127.0.0.1/cgi-bin/cgi1

000-default
 1 <VirtualHost *:80>
 2     ServerAdmin webmaster@localhost
 3 
 4     DocumentRoot /var/www
 5     <Directory />
 6         Options FollowSymLinks
 7         AllowOverride None
 8     </Directory>
 9     <Directory /var/www/>
10         Options Indexes FollowSymLinks MultiViews
11         AllowOverride None
12         Order allow,deny
13         allow from all
14     </Directory>
15 
16     ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
17     <Directory "/usr/lib/cgi-bin">
18         AllowOverride None
19         Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
20         Order allow,deny
21         Allow from all
22     </Directory>
23 
24     ErrorLog ${APACHE_LOG_DIR}/error.log
25 
26     # Possible values include: debug, info, notice, warn, error, crit,
27     # alert, emerg.
28     LogLevel warn
29 
30     CustomLog ${APACHE_LOG_DIR}/access.log combined
31 
32     Alias /doc/ "/usr/share/doc/"
33     <Directory "/usr/share/doc/">
34         Options Indexes MultiViews FollowSymLinks
35         AllowOverride None
36         Order deny,allow
37         Deny from all
38         Allow from 127.0.0.0/255.0.0.0 ::1/128
39     </Directory>
40 
41 </VirtualHost>

cgi1 為我寫的 cgi 測試程式
/usr/lib/cgi-bin/cgi1.c
1 #include <stdio.h>
2 
3 int main(int argc, char *argv[])
4 {
5   printf("Content-type: text/plain\n\n");
6   printf("cgi test\n");
7 
8   return 0;
9 }

在瀏覽器打上:
http://127.0.0.1/cgi-bin/cgi1
就可看到結果。

讓使用者可以用自己的目錄來寫 cgi 程式, 這需要修改一點東西:
/etc/apache2/mods-enabled/userdir.conf
 1 <IfModule mod_userdir.c>
 2         UserDir public_html
 3         UserDir disabled root
 4 
 5         <Directory /home/*/public_html>
 6                 AllowOverride FileInfo AuthConfig Limit Indexes
 7                 Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec 
 8                 <Limit GET POST OPTIONS>
 9                         Order allow,deny
10                         Allow from all
11                 </Limit>
12                 <LimitExcept GET POST OPTIONS>
13                         Order deny,allow
14                         Deny from all
15                 </LimitExcept>
16         </Directory>
17         <Directory /home/*/public_html/cgi-bin>
18                 SetHandler cgi-script
19                 Options ExecCGI
20 
21         </Directory>
22 </IfModule>

L17 ~ 21 是我加入的, 這樣可以使用這樣的 url 執行 cgi:

http://127.0.0.1/~descent/cgi-bin/cgi1

cgi1 放在 /home/descent/public_html/cgi-bin/

如果有問題的話, 那就是哪裡沒設定好, 我前面提過, 設定 apache/cgi 並不是一件容易的事情。

在大學時期, cgi 是很流行的技術, 那時候是 isapi, nsapi 還存在的時代 (這個相對難很多, 而且 linux 不能用), 學習 cgi 讓我吃足了苦頭, 因為程式本身就有點難度, 而架設 web server 則是另外的關卡, 那時候查到的資料大多是使用 perl, 不容易找到使用 c 的 cgi 程式, 而解說 cgi 原理的資料就更少了。

我那時候堅持使用 c 來完成 cgi, 以我當時的功力簡直就是找死, cgi 需要處理很多字串, c style string 的能力很原始, 剛學習 c 的我根本無法掌控好這部份。

通常寫 c cgi 會去找個 cgi library 來使用, 省去煩雜的 html 編碼轉換, 我用過的是 c++ cgicc, 這是 c++ 的 cgi library, 不錯用。

ref:
http://blog.jangmt.com/2010/08/apache2-cgiperlpythonbash-shell.html