[httpsperver]HTTP依頼の記事をどのように解析しますか?

6539 ワード

このhttp serverの実現ソースコードは私のgithubに置いています。興味があればクリックしてみてください。
前の記事では、どのようにして簡単なserverを作成するかについて述べましたが、このプログラムは要求を受けただけで、すぐに応答します。正常な開発においては、異なる要求に応じて異なる応答を行うべきである。上記の機能を実現するには、まずクライアントからの要求文を解析します。
新聞文は異なるコンテキストシナリオの下で異なる理解があり、本稿で言う新聞文はすべてHTTPコンテキストで記述された名詞である。
HTTPレポートは何ですか
HTTPプログラムでは、HTTPを用いて荷物を運ぶ小包として、プログラム間で情報を伝達する際に送信されるデータブロックとしても理解できます。これらのデータブロックは、いくつかのテキスト形式のメタ情報で始まり、これらの情報は、新聞の内容と意味を記述し、後に任意のデータ部分が続く。
新聞の流れ
HTTPの使用は、流入及び流出に属し、報文の伝達方向を記述する。HTTPレポートは合水のように流れます。時報を要求しても応答しても下流に流れます。すべての新聞の送信者は受信者の上流にいます。下図は、新聞文が下流に流れる例を示しています。
新聞の構成
新聞は三つの部分から構成されています。
  • 新聞について説明する開始行
  • は、属性を含むヘッダブロック
  • を含む。
  • オプション、データを含む本体部
  • 開始行とヘッダは、行によって分離されたASCIIテキストである。各行は、2つの文字(エコーコード13と改行記号--ASCIIコード10)からなる行の終端シーケンスで終了します。CRLFと書いてもいいです
    行の終了はCRLFで表されるべきだが、穏健なアプリケーションは行の区切りとして単一の行を受け入れるべきである。筆者はCRLFでの改行の解析だけをサポートしています。ルールがある以上、同じプロトコルに従ったプログラムが通信できると思います。
    エンティティは、任意のデータブロックである。開始行およびヘッダとは異なり、本体には本体またはバイナリデータが含まれてもよく、例えばGET 1ページまたはファイルだけが空であっても良い。
    新聞の文法の書式とルールを見てみます。
    新聞文の文法
    リクエストの構文:
      
    
    
    
    応答記事の文法:
      
    
    
    
    method、方法
    クライアントは、サーバがリソースに対して実行する動作を望んでいる。例えばGET、POST
    request-URL、URLを要求します。
    リソース、またはURLパスコンポーネントの完全なURLが要求される。
    バージョン
    新聞文で使用されているHTTPバージョンです。フォーマット:HTTP/..。ここで、major(主要バージョン番号)とminor(サブバージョン番号)は整数です。
    status-code、ステータスコード
    要求プロセスが発生した場合の数字を説明する。
    reason-phrase、原因フレーズ
    数字の状態コードのテキスト記述バージョン。
    ヘッド
    各ヘッダには名前が含まれています。後にはコロン(:)が付いています。そして任意のスペースです。次は値です。最後はCRLFです。ゼロまたは複数のヘッダがあり得る。ヘッダはCRLFで終了し、ヘッダの終了とエンティティ本体の開始を表す。
    entity-body、本体の本体部分
    任意のデータからなるデータブロックを含みます。いいえ、CRLFで終了します。
    要求行
    新聞の開始を要求する行を要求行といいます。すべてのHTTPレポートは一行の先頭行で始まります。要求行は、1つの方法と、1つの要求URLと、HTTPのバージョンの3つのフィールドを含む。各フィールドはスペースで区切られます。
    例えば、GET/HTTP/1.1。
    要求方法はGETで、要求URLは/、HTTPバージョンはHTTP/1.1です。
    応答行
    応答メッセージの先頭行を応答行と呼びます。応答行は、HTTPバージョン、デジタル状態コード、および動作状態を記述するテキスト形式の原因フレーズを含む。3つのフィールドもスペースで区切られます。
    例えば、HTTP/1.1 200 OKです。
    HTTPバージョンはHTTP/1.1で、数字の状態コードは200で、原因のフレーズはOKです。成功を願います。
    ヘッダ
    ヘッダは、要求及び応答メッセージに含まれる追加情報である。本質的には、彼らはキーペアのリストだけです。
    例えば、Contint-Length:19
    戻る内容の長さは19を表します。
    本体の本体部分
    簡単に言えば、この部分がHTTPの伝送する内容です。
    解析要求メッセージ
    新聞文がどのように構成されているかと各部分の代表の内容を知ってから、どのように解析して欲しいかという報告文を心の中に知っています。
    コアコード
        /*       */
        int parse_start_line(int sockfd, char *recv_buf, req_pack *rp)
        {
            char *p = recv_buf;
            char *ch = p;
            int i = 0;
            enum parts { method, url, ver } req_part = method;
            char *method_str;
            char *url_str;
            char *ver_str;
            int k = 0;
    
            if (*ch < 'A' || *ch > 'Z') {
                return -1;
            }
    
            while (*ch != CR) {
                if (*ch != BLANK) {
                    k++;
                } else if (req_part == method) {
                    method_str = (char *)malloc(k * sizeof(char *));
                    memset(method_str, 0, sizeof(char *));
                    strncpy(method_str, recv_buf, k);
                    k = 0;
                    req_part = url;
                } else if (req_part == url) {
                    url_str = (char *)malloc(k * sizeof(char *));
                    memset(url_str, 0, sizeof(char *));
                    strncpy(url_str, recv_buf + strlen(method_str) + 1, k);
                    k = 0;
                    req_part = ver;
                }
                ch++;
                i++;
            }
    
            if (req_part == url) {
                if (k != 0) {
                    url_str = (char *)malloc(k * sizeof(char));
                    memset(url_str, 0, sizeof(char));
                    strncpy(url_str, recv_buf + strlen(method_str) + 1, k);
                    k = 0;
                } else {
                    return -1;
                }
            }
    
            if (k == 0) {
                ver_str = (char *)malloc(8 * sizeof(char));
                memset(ver_str, 0, sizeof(char));
                strcpy(ver_str, "HTTP/1.1");
            } else {
                ver_str = (char *)malloc(k * sizeof(char));
                memset(ver_str, 0, sizeof(char));
                strncpy(ver_str,
                        recv_buf + strlen(method_str) + strlen(url_str) + 2, k);
            }
    
            rp->method = method_str;
            rp->url = url_str;
            rp->version = ver_str;
    
            return (i + 2);
        }
    
        /*        */
        int parse_header(int sockfd, char *recv_buf, header headers[])
        {
            char *p = recv_buf;
            char *ch = p;
            int i = 0;
            int k = 0;
            int v = 0;
            int h_i = 0;
            bool is_newline = false;
            char *key_str;
            char *value_str;
            header *tmp_header = (header *)malloc(sizeof(header *));
            memset(tmp_header, 0, sizeof(header));
    
            while (1) {
                if (*ch == CR && *(ch + 1) == LF) {
                    break;
                }
                while (*ch != COLON) {
                    ch++;
                    i++;
                    k++;
                }
                if (*ch == COLON) {
                    key_str = (char *)malloc(k * sizeof(char *));
                    memset(key_str, 0, sizeof(char *));
                    strncpy(key_str, recv_buf + i - k, k);
                    k = 0;
                    ch++;
                    i++;
                }
                while (*ch != CR) {
                    ch++;
                    i++;
                    v++;
                }
                if (*ch == CR) {
                    value_str = (char *)malloc(v * sizeof(char *));
                    memset(value_str, 0, sizeof(char *));
                    strncpy(value_str, recv_buf + i - v, v);
                    v = 0;
                    i++;
                    ch++;
                }
                i++;
                ch++;
                headers[h_i].key = key_str;
                headers[h_i].value = value_str;
                h_i++;
            }
    
            return (i + 2);
        }
    思想を解析する
    recvで受信した要求文字列を巡回して、リターン/rで一行のデータを判断するかどうかを確認します。
    開始行に対して、スペース区切りの異なるフィールドがあるかどうかを確認します。ヘッダに対して、番号を付けてキーのペアを分けるフィールドの値があるかどうかを確認します。エンティティの本体部分については、CRLF文字列があるかどうかを先に判断し、残りの内容をすべて本体の一部とする。
    戻り値は、次の巡回の開始位置を通知します。
    不正な要求行に遭遇した場合、400の応答が返される。
    締め括りをつける
    新聞文を解析する過程はHTTPプロトコルの規定に従って新聞文を解析し、新聞文に含まれている情報を取得することです。
    基礎知識が弱いので、コードには多くの誤りがあります。最適化が必要です。間違ったところを見たり、他のアドバイスがあれば、ぜひ教えてください。
    このhttp serverの実現ソースコードは私のgithubに置いています。興味があればクリックしてみてください。
    オリジナルの文章は、文筆に限りがあり、浅学で才能があり、文章に不備があれば、万望告知。
    この文章があなたの役に立つなら、紹介してください。ありがとうございます。