1

知りたいのですが、応答ストリームでヘッダーが終了する場所を見つける可能性はありますか?

質問の背景は次のとおりです。私は c でソケットを使用して Web サイトからコンテンツを取得しています。コンテンツは gzip でエンコードされています。コンテンツをストリームから直接読み取り、gzip コンテンツを zlib でエンコードしたいと考えています。しかし、gzip コンテンツが開始され、http ヘッダーが終了したことをどのように知ることができますか。

私の意見では、奇妙な結果が得られる2つの方法を大まかに試しました。最初に、ストリーム全体を読み取り、ターミナルで出力します。http ヘッダーは、予想どおり「\r\n\r\n」で終わりますが、2 回目は、ヘッダーを取得するために応答を 1 回取得するだけです。次に、while ループでコンテンツを読み取ります。ここでは、ヘッダーは「\r\n\r\n」なしで終了します。

なんで?そして、コンテンツを読む正しい方法はどれですか?

サーバーからの応答をどのように取得しているかを確認できるように、コードを提供します。

//first way (gives rnrn)
char *output, *output_header, *output_content, **output_result;
size_t size;
FILE *stream;
stream = open_memstream (&output, &size);
char BUF[BUFSIZ];
while(recv(socket_desc, BUF, (BUFSIZ - 1), 0) > 0)
{
    fprintf (stream, "%s", BUF);
}
fflush(stream);
fclose(stream);

output_result = str_split(output, "\r\n\r\n");
output_header = output_result[0];
output_content = output_result[1];

printf("Header:\n%s\n", output_header);
printf("Content:\n%s\n", output_content);

.

//second way (doesnt give rnrn)
char *content, *output_header;
size_t size;
FILE *stream;
stream = open_memstream (&content, &size);
char BUF[BUFSIZ];

if((recv(socket_desc, BUF, (BUFSIZ - 1), 0) > 0)
{
    output_header = BUF;
}

while(recv(socket_desc, BUF, (BUFSIZ - 1), 0) > 0)
{
    fprintf (stream, "%s", BUF); //i would just use this as input stream to zlib
}
fflush(stream);
fclose(stream);

printf("Header:\n%s\n", output_header);
printf("Content:\n%s\n", content);

どちらも同じ結果を端末に出力しますが、2番目の結果は、文字列の分割で失われるため、少なくともいくつかのブレークを出力するはずです。

私はcが初めてなので、簡単なことを監督するだけかもしれません。

4

1 に答える 1

8

recv()ソケットが切断されるか失敗するまで (そして受信したデータを間違った方法でストリームに書き込むまで) ループで呼び出し、すべての生データをchar*バッファーに格納します。特に HTTP キープアライブが使用されている場合 (この場合、応答の最後で切断は発生しません)、これは HTTP 応答を読み取る正しい方法ではありません。RFC 2616で概説されている規則に従う必要があります。すなわち:

  1. "\r\n\r\n"シーケンスが検出されるまで読み取ります。これにより、応答ヘッダーが終了します。それ以降のバイトはまだ読み取らないでください。

  2. RFC 2616 セクション 4.4のルールに従って、受信したヘッダーを分析します。これらは、残りの応答データの実際の形式を示しています。

  3. 残りのデータがある場合は、#2 で発見された形式に従って読み取ります。

  4. Connection: close応答が HTTP 1.1 を使用している場合は受信したヘッダーにヘッダーが存在するかどうConnection: keep-aliveかを確認し、応答が HTTP 0.9 または 1.0 を使用している場合はヘッダーがないことを確認します。検出された場合は、サーバーがソケット接続の端を閉じているため、ソケット接続の端を閉じます。それ以外の場合は、接続を開いたままにして、後続のリクエストに再利用します (接続の使用が終了しない限り、接続を閉じます)。

  5. 受信したデータを必要に応じて処理します。

つまり、代わりに次のようなことを行う必要があります (疑似コード):

string headers[];
byte data[];

string statusLine = read a CRLF-delimited line;
int statusCode = extract from status line;
string responseVersion = extract from status line;

do
{
    string header = read a CRLF-delimited line;
    if (header == "") break;
    add header to headers list;
}
while (true);

if ( !((statusCode in [1xx, 204, 304]) || (request was "HEAD")) )
{
    if (headers["Transfer-Encoding"] ends with "chunked")
    {
        do
        {
            string chunk = read a CRLF delimited line;
            int chunkSize = extract from chunk line;
            if (chunkSize == 0) break;

            read exactly chunkSize number of bytes into data storage;

            read and discard until a CRLF has been read;
        }
        while (true);

        do
        {
            string header = read a CRLF-delimited line;
            if (header == "") break;
            add header to headers list;
        }
        while (true);
    }
    else if (headers["Content-Length"] is present)
    {
        read exactly Content-Length number of bytes into data storage;
    }
    else if (headers["Content-Type"] begins with "multipart/")
    {
        string boundary = extract from Content-Type header;
        read into data storage until terminating boundary has been read;
    }
    else
    {
        read bytes into data storage until disconnected;
    }
}

if (!disconnected)
{
    if (responseVersion == "HTTP/1.1")
    {
        if (headers["Connection"] == "close")
            close connection;
    }
    else
    {
        if (headers["Connection"] != "keep-alive")
            close connection;
    }
}

check statusCode for errors;
process data contents, per info in headers list;
于 2013-04-27T00:34:07.953 に答える