HttpHeaderParser(2012/08/08)
HTTPリクエストのヘッダーパーサー
ウェブソケットはハンドシェイクの際に、以下のようなHTTPヘッダーを送信してくる。この情報に基づいて、サーバー側では接続とハンドシェイクを行う。
送られてきたHTTPヘッダーはパーサーを介してサーバープログラムで簡易に使用出来るようにする。
WebSocketクライアントから送られてくるHTTPヘッダ
GET /chat HTTP/1.1\r\n
Host: server.example.com\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Key: XXXXXXXXXXXXXXXXXXXXXX==\r\n
Origin: http://example.com\r\n
Sec-WebSocket-Protocol: chat, superchat\r\n
Sec-WebSocket-Version: 13\r\n
以下にHttpHeaderParserのソースコードを掲載する。
使い方としては
- CHttpHeaderParserのインスタンスを作成
- AddDataを使用して、受信したHTTPヘッダーを登録
- ヘッダー1行目のメソッドはGetMethod()を使用して取得
- リクエストURIはGetRequestURI()を使用して取得
- HTTPバージョンはGetHTTPVersion()を使用して取得
- 各ヘッダーフィールドはGetFieldValueにフィールド文字を指定して取得
となる。
HttpHeaderParser.h
#pragma once
#include "Tool.h"
class CHttpHeaderElement
{
friend class CHttpHeaderParser;
protected:
char * m_pszField;
char * m_pszValue;
CHttpHeaderElement * m_pNextElement;
public:
CHttpHeaderElement();
~CHttpHeaderElement();
void SetField( const char * pcszField , int iStartIndex, int iLength );
void SetValue( const char * pcszValue , int iStartIndex, int iLength );
};
class CHttpHeaderParser
{
protected:
CHttpHeaderElement * m_pHeaderElement, *m_pHeaderElementLast;
bool m_bFirstData;
char * m_pszMethod;
char * m_pszRequestURI;
char * m_pszHTTPVersion;
void SetFieldAndValue( const char * pcszData, int iStartIndex, int iLength );
void SetRequestHeader( const char * pcszData, int iStartIndex, int iLength );
public:
CHttpHeaderParser();
~CHttpHeaderParser();
void ClearData();
void AddData( const char * pcszData );
void AddData( const char * pcszData , int iLength);
const char * GetFieldValue( const char * pcszField );
const char * GetMethod();
const char * GetRequestURI();
const char * GetHTTPVersion();
};
HttpHeaderParser.cpp
#include <string.h>
#include "HttpHeaderParser.h"
#include "Tool.h"
//
// コンストラクタ
//
// 内部データの初期化
//
CHttpHeaderElement::CHttpHeaderElement()
{
m_pszField = NULL;
m_pszValue = NULL;
m_pNextElement = NULL;
}
//
// デストラクタ
//
CHttpHeaderElement::~CHttpHeaderElement()
{
CTool::ReleaseStringA( &m_pszField );
CTool::ReleaseStringA( &m_pszValue );
}
//
// フィールドデータの登録
//
// 引数:
// pcszField (IN) 登録するフィールドデータ
//
void CHttpHeaderElement::SetField( const char * pcszField , int iStartIndex, int iLength)
{
CTool::SetStringA( &m_pszField, pcszField , iStartIndex, iLength);
}
//
// バリューデータの登録
//
// 引数:
// pcszValue (IN) 登録する値データ
//
void CHttpHeaderElement::SetValue( const char * pcszValue , int iStartIndex, int iLength)
{
CTool::SetStringA( &m_pszValue, pcszValue , iStartIndex, iLength);
}
///////////////////////////////////////////////////////////////////////////////
//
// コンストラクタ
//
CHttpHeaderParser::CHttpHeaderParser()
{
m_pHeaderElement = NULL;
m_pszMethod = m_pszRequestURI = m_pszHTTPVersion = NULL;
m_bFirstData = true;
}
//
// デストラクタ
//
CHttpHeaderParser::~CHttpHeaderParser()
{
ClearData();
}
//
// 内部データをクリアする
//
// リクエストデータおよびフィールド&バリューを削除する
//
void CHttpHeaderParser::ClearData()
{
while( m_pHeaderElement )
{
CHttpHeaderElement * pDelete = m_pHeaderElement;
m_pHeaderElement = m_pHeaderElement->m_pNextElement;
delete pDelete;
}
CTool::ReleaseStringA( &m_pszMethod );
CTool::ReleaseStringA( &m_pszRequestURI );
CTool::ReleaseStringA( &m_pszHTTPVersion );
m_pHeaderElementLast = NULL;
m_bFirstData = true;
}
//
// ヘッダー文字列を使って、データを登録する
//
// 引数:
// pcszData (IN) ヘッダーデータ
//
void CHttpHeaderParser::AddData( const char * pcszData )
{
AddData( pcszData, (int)strlen( pcszData ) );
}
void CHttpHeaderParser::AddData( const char * pcszData , int iLength)
{
int iStartIndex = 0;
bool bSetStartIndex = true;
for( int i=0 ; i<iLength ; i++ )
{
if( bSetStartIndex )
{
iStartIndex = i;
}
bSetStartIndex = false;
//改行コードの場合
if( pcszData[i] == '\r' && pcszData[i+1] == '\n' )
{
if( m_bFirstData )
{
SetRequestHeader( pcszData, iStartIndex, i-iStartIndex+1 );
m_bFirstData = false;
}
else
{
SetFieldAndValue( pcszData, iStartIndex, i-iStartIndex+1);
}
i++;
bSetStartIndex = true;
}
}
}
//
// 先頭行(リクエストヘッダ)をメソッド、URI、HTTPバージョンに分割して登録する
//
void CHttpHeaderParser::SetRequestHeader( const char * pcszData, int iStartIndex, int iLength )
{
int iSectionStartIndex = iStartIndex;
int iCounter = 0;
for( int i=iStartIndex ; i<iLength+1 ; i++ )
{
if( i == iLength || pcszData[i] == ' ' )
{
char ** ppszRegist = NULL;
switch( iCounter )
{
//メソッドの場合
case 0: ppszRegist = &m_pszMethod; break;
case 1: ppszRegist = &m_pszRequestURI; break;
case 2: ppszRegist = &m_pszHTTPVersion; break;
break;
}
if( ppszRegist )
{
CTool::SetStringA( ppszRegist, pcszData, iSectionStartIndex, i-iSectionStartIndex+1 );
}
iSectionStartIndex = i+1;
iCounter++;
}
}
}
//
// 1行データを解析し、フィールドと値に分けてCHttpHeaderElementに変換後内部リストに登録する
//
// 引数:
// pcszData (IN)
void CHttpHeaderParser::SetFieldAndValue( const char * pcszData, int iStartIndex, int iLength )
{
int iFieldStart = -1;
int iFieldLength = -1;
int iValueStart = -1;
int iValueLength = -1;
for( int i=iStartIndex ; i<iStartIndex+iLength ; i++ )
{
char cTemp = pcszData[i];
//フィールド区切りの場合
if( cTemp == ':' )
{
//フィールド開始が登録されていない場合(行にいきなりフィールド区切りが出てくる)
if( iFieldStart < 0 )
{
return;
}
iFieldLength = i - iStartIndex;
}
//空白スペースでない場合
else if( cTemp != ' ' && cTemp != '\t' )
{
//フィールド開始が登録されていない場合(フィールド開始の登録)
if( iFieldStart < 0 )
{
iFieldStart = i;
}
//フィールド長さが登録されており、値開始が登録されていない場合(値開始の登録)
else if( iFieldLength >= 0 && iValueStart < 0 )
{
iValueStart = i;
}
}
}
//値開始が正常に登録されている場合は値長さを文字列終端とする。
if( iValueStart >= 0 )
{
iValueLength = iStartIndex + iLength - iValueStart - 1;
}
//フィールド部分のトリム処理
for( int i=iFieldStart+iFieldLength-1 ; i>=iFieldStart ; i-- )
{
if( pcszData[i] == ' ' || pcszData[i] == '\t' )
{
iFieldLength--;
}
else
{
break;
}
}
//トリムの結果フィールド長さが無くなってしまった場合
if( iFieldLength <= 0 )
{
return;
}
//データの追加登録処理
CHttpHeaderElement * pElement = new CHttpHeaderElement();
pElement->SetField( pcszData, iFieldStart, iFieldLength );
pElement->SetValue( pcszData, iValueStart, iValueLength );
//リストに登録する
if( m_pHeaderElement )
{
m_pHeaderElementLast->m_pNextElement = pElement;
m_pHeaderElementLast = pElement;
}
else
{
m_pHeaderElement = m_pHeaderElementLast = pElement;
}
}
//
// 指定フィールドの値を取得する
//
// 引数:
// pcszField (IN) 値を取得するフィールド名
//
// 戻り値:
// !=NULL : フィールドに対応する値
// ==NULL : 該当フィールドデータなし
//
const char * CHttpHeaderParser::GetFieldValue( const char * pcszField )
{
for( CHttpHeaderElement * pElement = m_pHeaderElement
; pElement
; pElement = pElement->m_pNextElement )
{
if( strcmp( pElement->m_pszField, pcszField ) == 0 )
{
return pElement->m_pszValue;
}
}
return NULL;
}
//
// メソッドのゲッタ
//
const char * CHttpHeaderParser::GetMethod()
{
return m_pszMethod;
}
//
// リクエストURIのゲッタ
//
const char * CHttpHeaderParser::GetRequestURI()
{
return m_pszRequestURI;
}
//
// HTTPバージョンのゲッタ
//
const char * CHttpHeaderParser::GetHTTPVersion()
{
return m_pszHTTPVersion;
}