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()
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のこのようなポーリングメカニズムにより、カードを切らずに複数のリクエストを処理できるようになった)