質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.47%

  • C

    3833questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • HTTP

    562questions

    HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

HTTPサーバでのPOSTが来た際の処理

受付中

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 1,389

Subaru

score 10

POSTリクエストが来た際に標準入力からcgiにパラメータを渡すということはわかりますが
具体的にどのように処理をしていいのかわかりません。教えてください
また、今回のプログラムはGET、POSTの両方でcgiにパラメータを渡して処理できるHTTPサーバを目指しています。プログラムに不備があれば指摘してくれればと思います。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/fcntl.h>              
#include <sys/wait.h>
#include <sys/types.h>
#include <arpa/inet.h>

#define MAXPENDING 5

void DieWithError(char *errorMessage);
int CreateTCPServerSocket(unsigned short port);
int AcceptTCPConnection(int servSock);
int sendMessage(int fd, char *msg);
void sendHeader(int clntSock);
int http(int clntSock);
void cgiMethod(int clntSock, char *uri_file, char *value);


/* ========================== */

int main( int argc,char *argv[] ) 
{
    int servSock;
    int clntSock;
    unsigned short ServPort;
    
    int clnt_len;
    int rc = 0;
    pid_t processID;
    unsigned int ProcessCount = 0;
    
    struct sockaddr_in serv_addr;           
    struct sockaddr_in clnt_addr;
    
    // serverPort
    if( argc != 2 )
    {
        fprintf(stderr, "Usage: %s <server port>\n",argv[0]);
        exit(1);
    }
    
    ServPort = atoi(argv[1]);
    servSock = CreateTCPServerSocket(ServPort);
    
    while(1)                                                     
    {
        clntSock = AcceptTCPConnection(servSock);
        
        if ( (processID = fork()) < 0 )
        {
            fprintf(stderr, "error: fork() failed");
            return 1;
        }
        else if ( processID == 0 )
        {
            close(servSock);
            http(clntSock);
            return 0;
        }
        
        // 親プロセス
        printf("with process1: %d\n", (int) processID);
        close(clntSock);                                           
        ProcessCount++;                                      
        
        //プロセスの回収
        while( ProcessCount )
        {
            processID = waitpid((pid_t) -1, NULL, WNOHANG);
            if(processID < 0)
            {                                                       
                fprintf(stderr, "error: waitpid() failed");
                break;
            }
            else if(processID == 0)                                
                break;
            else
                ProcessCount--;
        }
    }
}

/* ========================== */


/* errerMessage */
void DieWithError(char *errorMessage){
    perror(errorMessage);
    exit(1);
}

/* soket,bind,listen */
int CreateTCPServerSocket(unsigned short port){
    
    int sock;
    struct sockaddr_in echoServAddr;
    
    if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        DieWithError("socket() failed");
    
    // 構造体の初期化
    memset(&echoServAddr, 0, sizeof(echoServAddr));
    
    echoServAddr.sin_family = AF_INET;
    echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    echoServAddr.sin_port = htons(port);
    
    // localAddrへbind
    if(bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
        DieWithError("bind() failed");
    
    if(listen(sock, MAXPENDING) < 0)
        DieWithError("listen() failed");
    
    return sock;
}

/* Accept */
int AcceptTCPConnection(int servSock){
    int clntSock;
    struct sockaddr_in echoClntAddr;
    unsigned int clntLen;
    
    clntLen = sizeof(echoClntAddr);
    
    if((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0)
        DieWithError("accept() failed");
    
    printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));
    
    return clntSock;
}

void sendHeader( int clntSock ) {
    sendMessage( clntSock, "HTTP/1.1 200 OK\r\n" );
    sendMessage( clntSock, "Content-type: text/html; charset=UTF-8\r\n" );
    sendMessage( clntSock, "\r\n" );
}

int sendMessage(int fd, char *msg){
    int msg_len;
    msg_len = strlen(msg);
        
    if ( write(fd, msg, msg_len) != msg_len )
    {
        fprintf(stderr, "error: writing.");
    }
    return msg_len;
}

int http(int clntSock){
    int len;
    int read_fd;
    char buf[1024];
    pid_t processID;                    
    unsigned int ProcessCount = 0;
    
    char method_name[16];
    char uri_addr[256];
    char http_ver[64];
    char *uri_file;
    char *value;
    
    if(read(clntSock, buf, 1024) <= 0 )                  
    {
        fprintf(stderr, "error: reading a request.\n");
        exit(1);
    } 
    
    sscanf(buf, "%s %s %s", method_name, uri_addr, http_ver);
    //printf("%s %s %s\n", method_name, uri_addr, http_ver);
    
    uri_file = uri_addr + 1;
    
    if (strcmp(method_name, "GET") == 0)
    {
        if (strstr(uri_addr, ".cgi") != NULL)
        {
            uri_file = strtok(uri_file, "?");      
            value = strtok(NULL, "");
            cgiMethod(clntSock, uri_file, value);            
            close(clntSock);              
        }
        else{
            if ((read_fd = open(uri_file, O_RDONLY, 0666)) < 0)
            {
                sendMessage(clntSock, "404 Not Found");
                return 1;
            }
            
            sendHeader(clntSock);
            
            while ((len = read(read_fd, buf, 1024)) > 0)
            {
                if (write(clntSock, buf, len) != len)
                {
                    fprintf(stderr, "error: writing a response.\n");
                    break;
                }
            }
            close(read_fd);
        }
    }
    else if ( strcmp(method_name, "POST" ) == 0) {
    
        //
        //ここをどのように書くかわからない
        //

        close(clntSock);
    }
    else {
        sendMessage(clntSock, "501 Not Implemented");
        exit(1);
    }
}

void cgiMethod(int clntSock, char *uri_file, char *value){
    
    pid_t processID;
    unsigned int ProcessCount = 0;
    int fd[2];
    int len;
    char buf[1024];
    
    
    pipe(fd);
    
    if((processID = fork()) < 0)
    {
        fprintf(stderr, "error: fork() failed");
        exit(1);
    }
    else if(processID == 0)                
    {
        close(fd[0]);
        dup2(fd[1], 1);
        
        setenv("QUERY_STRING" ,value, 1);
        
        if (execl(uri_file, "", NULL) < 0)
        {
            fprintf(stderr, "error: execl() failed\n");
            exit(1);
        }

        close(clntSock);              
        ProcessCount++;
        exit(0);
    }
    
    close(fd[1]);
    
    sendHeader(clntSock);
    while((len = read(fd[0], buf, sizeof(buf))) > 0)      
    {
        if(write(clntSock, buf, len) != len)             
        {   
            fprintf(stderr, "error: writing a response.\n");
            break;
        }
    }
    
    // プロセスの回収
    printf("with process2: %d\n", (int) processID);
    
    while(ProcessCount)
    {
        processID = waitpid((pid_t) -1, NULL, WNOHANG);
        if(processID < 0)
        {                                                       
            fprintf(stderr, "error: waitpid() failed");
            break;
        }
        else if(processID == 0)                                
            break;
        else
            ProcessCount--;
    }
    
    close(fd[0]);
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

0

clntSockからreadしたものを、先頭からきちんとHTTPプロトコルに従って解析して、リクエストボディ部分をPOSTデータとします。


あと、アドバイスとしては、read(clntSock,buf,1024)するのでなく、fdopenを使って、
FILE *fp;
~~~
if(!(fp=fdopen(clntSock,"r"))){
  エラー処理
}
if(!fgets(buf,1024,fp)){
  エラー処理
}
のようにfgetsで行単位で読んだ方が、リクエストやリクエストヘッダの解析をするときに楽です。
空行の後ろが、ボディ。
ボディを読むときは、fgetsでなくfreadで。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

関連した質問

同じタグがついた質問を見る

  • C

    3833questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • HTTP

    562questions

    HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。