boaサーバによるCGI機能の実現
7322 ワード
CGIの概要
CGIはWebサーバの実行時の外部プログラムの仕様であり、CGIによって作成されたプログラムはサーバ機能を拡張することができる.CGIアプリケーションはブラウザと対話することができ、データベースAPIを介してデータベースサーバなどの外部データソースと通信し、データベースサーバからデータを取得することもできる.HTMLドキュメントにフォーマットしてブラウザに送信するか、ブラウザから取得したデータをデータベースに格納することもできます.ほとんどのサーバはCGIをサポートしており、流行のC、C++、VB、Delphiなど、任意の言語でCGIを記述できます.CGIには、標準CGIと間接CGIの2種類があります.標準CGIはコマンドラインパラメータまたは環境変数を使用してサーバーの詳細要求を表し、サーバーとブラウザの通信は標準入出力方式を採用する.間接CGIはバッファCGIとも呼ばれ、CGIプログラムとCGIインタフェースの間にバッファを挿入し、バッファとCGIインタフェースの間に標準入出力で通信する.
簡単に言えば、CGIはwebサーバーが呼び出すことができる他のプログラムで、このプログラムはリダイレクトを出力して、このプログラムの中でprintfを通じてhtmlを生成して、パイプを通じてwebサーバーにデータを送って、インタラクションを完成します.
boa CGI実装
1.boaがデータを取得した場合、process_を実行するrequests()-->httpヘッダread_の読み込みheader()--->process_header_end();ヘッダ情報に基づいて、この要求がCGIであることを取得する
2.init_の実行cgi()
PIPE_READ、パイプのデータを読み込む準備をします.読み込み後、すべての読み込みが完了するまでポーリングを続け、ステータスを
PIPE_WRITEは、クライアントにデータを送信します.(boaのこのようなポーリングメカニズムにより、カードを切らずに複数のリクエストを処理できるようになった)
CGIはWebサーバの実行時の外部プログラムの仕様であり、CGIによって作成されたプログラムはサーバ機能を拡張することができる.CGIアプリケーションはブラウザと対話することができ、データベースAPIを介してデータベースサーバなどの外部データソースと通信し、データベースサーバからデータを取得することもできる.HTMLドキュメントにフォーマットしてブラウザに送信するか、ブラウザから取得したデータをデータベースに格納することもできます.ほとんどのサーバはCGIをサポートしており、流行のC、C++、VB、Delphiなど、任意の言語でCGIを記述できます.CGIには、標準CGIと間接CGIの2種類があります.標準CGIはコマンドラインパラメータまたは環境変数を使用してサーバーの詳細要求を表し、サーバーとブラウザの通信は標準入出力方式を採用する.間接CGIはバッファCGIとも呼ばれ、CGIプログラムとCGIインタフェースの間にバッファを挿入し、バッファとCGIインタフェースの間に標準入出力で通信する.
簡単に言えば、CGIはwebサーバーが呼び出すことができる他のプログラムで、このプログラムはリダイレクトを出力して、このプログラムの中でprintfを通じてhtmlを生成して、パイプを通じてwebサーバーにデータを送って、インタラクションを完成します.
boa CGI実装
1.boaがデータを取得した場合、process_を実行するrequests()-->httpヘッダread_の読み込みheader()--->process_header_end();ヘッダ情報に基づいて、この要求がCGIであることを取得する
2.init_の実行cgi()
int init_cgi(request * req)
{
int child_pid;
int pipes[2];
int use_pipes = 0;
SQUASH_KA(req);
if (req->is_cgi) {
if (complete_env(req) == 0) { //
return 0;
}
}
#ifdef FASCIST_LOGGING
{
int i;
for (i = 0; i < req->cgi_env_index; ++i)
fprintf(stderr, "%s - environment variable for cgi: \"%s\"
",
__FILE__, req->cgi_env[i]);
}
#endif
if (req->is_cgi == CGI || 1) {
use_pipes = 1; //
if (pipe(pipes) == -1) {
log_error_time();
perror("pipe");
return 0;
}
/* set the read end of the socket to non-blocking */
if (set_nonblock_fd(pipes[0]) == -1) { //
log_error_time();
perror("cgi-fcntl");
close(pipes[0]);
close(pipes[1]);
return 0;
}
}
child_pid = fork(); // , cgi , boa
switch(child_pid) {
case -1:
/* fork unsuccessful */
log_error_time();
perror("fork");
if (use_pipes) {
close(pipes[0]);
close(pipes[1]);
}
send_r_error(req);
/* FIXME: There is aproblem here. send_r_error would work
for NPH and CGI, but not for GUNZIP. Fix that. */
/* i'd like to send_r_error, but.... */
return 0;
break;
case 0:
/* child */
if (req->is_cgi == CGI || req->is_cgi == NPH) {
char *foo = strdup(req->pathname);
char *c;
if (!foo) {
WARN("unable to strdup pathname for req->pathname");
_exit(1);
}
c = strrchr(foo, '/');
if (c) {
++c;
*c = '\0';
} else {
/* we have a serious problem */
log_error_time();
perror("chdir");
if (use_pipes)
close(pipes[1]);
_exit(1);
}
if (chdir(foo) != 0) {
log_error_time();
perror("chdir");
if (use_pipes)
close(pipes[1]);
_exit(1);
}
}
if (use_pipes) {
close(pipes[0]);
/* tie cgi's STDOUT to it's write end of pipe */
if (dup2(pipes[1], STDOUT_FILENO) == -1) { // , cgi printf,
log_error_time();
perror("dup2 - pipes");
close(pipes[1]);
_exit(1);
}
close(pipes[1]);
if (set_block_fd(STDOUT_FILENO) == -1) { //
log_error_time();
perror("cgi-fcntl");
_exit(1);
}
} else {
/* tie stdout to socket */
if (dup2(req->fd, STDOUT_FILENO) == -1) {
log_error_time();
perror("dup2 - fd");
_exit(1);
}
/* Switch socket flags back to blocking */
if (set_block_fd(req->fd) == -1) {
log_error_time();
perror("cgi-fcntl");
_exit(1);
}
}
/* tie post_data_fd to POST stdin */
if (req->method == M_POST) { /* tie stdin to file */
lseek(req->post_data_fd, SEEK_SET, 0);
dup2(req->post_data_fd, STDIN_FILENO);
close(req->post_data_fd);
}
/* Close access log, so CGI program can't scribble
* where it shouldn't
*/
close_access_log();
/*
* tie STDERR to cgi_log_fd
* cgi_log_fd will automatically close, close-on-exec rocks!
* if we don't tied STDERR (current log_error) to cgi_log_fd,
* then we ought to close it.
*/
if (!cgi_log_fd)
dup2(devnullfd, STDERR_FILENO);
else
dup2(cgi_log_fd, STDERR_FILENO);
if (req->is_cgi) {
char *aargv[CGI_ARGC_MAX + 1];
create_argv(req, aargv);
execve(req->pathname, aargv, req->cgi_env); // cgi
} else {
if (req->pathname[strlen(req->pathname) - 1] == '/')
execl(dirmaker, dirmaker, req->pathname, req->request_uri,
NULL);
#ifdef GUNZIP
else
execl(GUNZIP, GUNZIP, "--stdout", "--decompress",
req->pathname, NULL);
#endif
}
/* execve failed */
WARN(req->pathname);
_exit(1);
break;
default:
/* parent */
/* if here, fork was successful */
if (verbose_cgi_logs) {
log_error_time();
fprintf(stderr, "Forked child \"%s\" pid %d
",
req->pathname, child_pid);
}
if (req->method == M_POST) {
close(req->post_data_fd); /* child closed it too */
req->post_data_fd = 0;
}
/* NPH, GUNZIP, etc... all go straight to the fd */
if (!use_pipes)
return 0;
close(pipes[1]);
req->data_fd = pipes[0]; // fd
req->status = PIPE_READ; // PIPE_READ
if (req->is_cgi == CGI) {
req->cgi_status = CGI_PARSE; /* got to parse cgi header */
/* for cgi_header... I get half the buffer! */
req->header_line = req->header_end =
(req->buffer + BUFFER_SIZE / 2);
} else {
req->cgi_status = CGI_BUFFER;
/* I get all the buffer! */
req->header_line = req->header_end = req->buffer;
}
/* reset req->filepos for logging (it's used in pipe.c) */
/* still don't know why req->filesize might be reset though */
req->filepos = 0;
break;
}
return 1; //more to do
}
3.次のポーリングでは、プログラムはprocessに戻ります.request、ステータスPIPE_READ、パイプのデータを読み込む準備をします.読み込み後、すべての読み込みが完了するまでポーリングを続け、ステータスを
PIPE_WRITEは、クライアントにデータを送信します.(boaのこのようなポーリングメカニズムにより、カードを切らずに複数のリクエストを処理できるようになった)