/*++

   Copyright    (c)    1996    Microsoft Corporation

   Module  Name :
      httphdr.hxx

   Abstract:
      This module declares all the variables and functions
       for Dictionary manager.
      It also defines the structure for handling HTTP headers

   Author:

       Murali R. Krishnan    ( MuraliK )    8-Nov-1996

   Environment:
       User - Win32

   Project:

       Internet Server DLL

   Revision History:

--*/

# ifndef _DICT_HXX_
# define _DICT_HXX_

/************************************************************
 *     Include Headers
 ************************************************************/

# include "buffer.hxx"
# include "dbgutil.h"


//
// Format of entries in the ALL_HTTP_FAST_MAP_HEADERS is
//  HfmHeader( enumeration-id, string)
//

# define ALL_HTTP_FAST_MAP_HEADERS()            \
    HfmHeader( HM_MET, "method")                \
    HfmHeader( HM_URL, "url")                   \
    HfmHeader( HM_VER, "version")               \
                                                \
    HfmHeader( HM_ACC, "Accept:")               \
    HfmHeader( HM_ACL, "Accept-Language:")      \
    HfmHeader( HM_CON, "Connection:")           \
    HfmHeader( HM_HST, "Host:")                 \
    HfmHeader( HM_REF, "Referer:")              \
    HfmHeader( HM_UAT, "User-Agent:")           \
                                                \
    HfmHeader( HM_PRG, "Pragma:")               \
    HfmHeader( HM_COK, "Cookie:")               \
    HfmHeader( HM_AUT, "Authorization:")        \
                                                \
    HfmHeader( HM_IMS, "If-Modified-Since:")    \
                                                \
    HfmHeader( HM_UPX, "UA-pixels:")            \
    HfmHeader( HM_UCL, "UA-color:")             \
    HfmHeader( HM_UOS, "UA-OS:")                \
    HfmHeader( HM_CPU, "UA-CPU:")               \
                                                \
    HfmHeader( HM_CLE, "Content-Length:")       \
    HfmHeader( HM_CTY, "Content-Type:")         \
                                                \
    HfmHeader( HM_PRA, "Proxy-Authorization:")  \
    HfmHeader( HM_PRC, "Proxy-Connection:")     \
                                                \
    HfmHeader( HM_RNG, "Range:")                \
    HfmHeader( HM_IFR, "If-Range:")             \
    HfmHeader( HM_IFM, "If-Match:")             \
    HfmHeader( HM_INM, "If-None-Match:")        \
    HfmHeader( HM_UMS, "Unless-Modified-Since:")\
    HfmHeader( HM_IUM, "If-Unmodified-Since:")  \
                                                \
    HfmHeader( HM_TEC, "Transfer-Encoding:")    \
    HfmHeader( HM_VIA, "Via:")                  \
    HfmHeader( HM_FWD, "Forwarded:")            \
    HfmHeader( HM_LCK, "Lock-Token:")           \
    HfmHeader( HM_TRN, "Translate:")            \
    HfmHeader( HM_ISM, "If:")                   \
    HfmHeader( HM_ACE, "Accept-Encoding:")      \


/*------------------------------------------------------------
 * AVG_HTTP_HEADER_LEN
 * o  empirically calculated based on the header-len of various
 *    headers specified in ALL_HTTP_FAST_MAP_HEADERS
 *   If you add a new header, longer than the average value, make
 *     sure you update the average as well!!!
 *   Include the terminating null-char in your calculation
 ------------------------------------------------------------*/
# define AVG_HTTP_HEADER_LEN    (14)


// generate the enumeration IDs
# define HfmHeader( HfmId, HfmString)       HfmId,

enum HTTP_FAST_MAP_HEADERS {

    ALL_HTTP_FAST_MAP_HEADERS()

    MAX_HTTP_FAST_MAP_HEADERS
}; // enum HTTP_FAST_MAP_HEADERS

# undef HfmHeader


inline BOOL IsValidHfm( IN HTTP_FAST_MAP_HEADERS hfm)
{
    return ( (HM_MET <= hfm) && ( hfm < MAX_HTTP_FAST_MAP_HEADERS));
}

//
// _HTTP_IS_LINEAR_SPACE()
//  Given a character, this function tests if this character is in
//  linear white-space (\t or ' ') character.
// It optimizes for the case where the character is not a linear white-space
//
inline BOOL _HTTP_IS_LINEAR_SPACE( CHAR c)
{ return ( ((c) <= ' ') && (((c) == ' ') || ((c) == '\t'))); }

/************************************************************
 *   Type Definitions
 ************************************************************/

/*++

  Dictionary:  provides storage for <name, value> pairs.
  Goals: Insert and lookup has to be fast.
  Amount of memory consumed should be kept as low as possible.

  Objects:
    DICTIONARY_MAPPER
      -  provides a mapping between the string name and ordinal for fast-mapping.
      -  one instance per collection of <string, ordinal>s.

    DICTIONARY
      provides the storage and lookup for a given collection
      Multiple instances of this object can be created, one per input datum.

 --*/


class DICTIONARY_MAPPER {

public:

    dllexp
    virtual  ~DICTIONARY_MAPPER(VOID) {}

    dllexp
    virtual BOOL Initialize( VOID) = 0;

    dllexp virtual
    BOOL FindOrdinal( IN LPCSTR pszName,
                      IN INT    cchName,
                      OUT DWORD * pdwOrdinal) const = 0;

    dllexp virtual
    LPCSTR FindName( IN DWORD dwOrdinal) const = 0;

    dllexp virtual
    DWORD NumItems( VOID) const = 0;

    dllexp virtual
    VOID PrintToBuffer( IN CHAR * pchBuffer,
                        IN OUT LPDWORD pcch) const = 0;

    dllexp virtual VOID Print( VOID) const = 0;

}; // class DICTIONARY_MAPPER



/*++
  class HTTP_HEADER_MAPPER

  o  Defines the mapper for mapping the HTTP header names to ordinals.

--*/

class HTTP_HEADER_MAPPER : public DICTIONARY_MAPPER {

public:

    HTTP_HEADER_MAPPER( IN DWORD sigChar)
        : m_nItems    ( 0),
          m_nSigChars ( sigChar),
          m_rgOnNameMapper ( NULL),
          DICTIONARY_MAPPER()
    {
    }

    dllexp virtual ~HTTP_HEADER_MAPPER(VOID);

    dllexp
    virtual BOOL Initialize( VOID);

    dllexp virtual
    BOOL FindOrdinal( IN LPCSTR pszName,
                      IN INT    cchName,
                      OUT DWORD * pdwOrdinal) const;

    dllexp virtual
    LPCSTR FindName( IN DWORD dwOrdinal) const;

    dllexp virtual
    DWORD NumItems( VOID) const { return ( m_nItems); }

    dllexp virtual
    VOID PrintToBuffer( IN CHAR * pchBuffer,
                        IN OUT LPDWORD pcch) const;
    dllexp virtual VOID Print( VOID) const;

    DWORD SizeOfNameMapper( VOID) const { return (m_nSigChars * m_nSigChars); }
    VOID SetSigChars( IN DWORD sigChars) { m_nSigChars = sigChars; }

private:
    DWORD  m_nItems;    // # items in the linear space
    DWORD  m_nSigChars;
    // same as g_OnFieldNameMapper[]  for HTTP headers
    // int  m_rgOnNameMapper[ _HTTP_HEADER_SIG_CHARS * _HTTP_HEADER_SIG_CHARS];
    int * m_rgOnNameMapper;

}; // class HTTP_HEADER_MAPPER



# define MAX_HEADERS_PER_CHUNK   ( 10)

//
// The following parameter DEFAULT_HTTP_HEADER_LEN is set using empirical data
// to cover the 80% of requests coming in. This definitely includes the requests
// used by performance benchmarks.
// Any revision or change in HTTP or traffic requires that this parameter be
//  tweaked.

# define DEFAULT_HTTP_HEADER_LEN ( 1 * 512) // 0.5 KB

// Minimum bytes to maintain in the buffer object of HttpHeader Dictionary
# define HH_MIN                (0)  // 4 KB
# define HH_GROW_BY            (1 * 1024)  // 1 KB
//Maximum bytes to maintain in the buffer object of the HttpHeader Dictionary
#define HH_MAX                 (64 * 1024) //64 KB

struct NAME_VALUE_PAIR {
    const CHAR * pchName;     // pointer to start of the name
    DWORD        cchName;     // length of the name
    LPCSTR       pchValue;    // pointer to value block
    DWORD        cchValue;    // length of the value block

}; // NAME_VALUE_PAIR



struct HH_ITERATOR {
    PVOID pChunk;
    DWORD  dwOrdinal;
    DWORD  dwPair;

    NAME_VALUE_PAIR np;
}; // struct HH_ITERATOR



class NAME_VALUE_CHUNK {

public:
    NAME_VALUE_CHUNK(VOID)
    {
        Reset();
    }

    VOID Reset( VOID)
    {
        InitializeListHead( & m_listEntry);
        ZeroMemory( m_rgNVP, sizeof( m_rgNVP));
        m_nPairs = 0;
    }

    BOOL IsSpaceAvailable( VOID) const
    { return ( m_nPairs < MAX_HEADERS_PER_CHUNK); }

    BOOL AddEntry( IN const CHAR * pszHeader, IN DWORD cchHeader,
                   IN const CHAR * pszValue,  IN DWORD cchValue
                   );

    dllexp NAME_VALUE_PAIR *
    FindEntry( IN const CHAR * pszHeader, IN DWORD cchHeader);

    dllexp NAME_VALUE_PAIR *
    FindMatchingOrFreeEntry( IN const CHAR * pszHeader,
                             IN DWORD   cchHeader,
                             IN LPBOOL  pfFound );

    dllexp DWORD PrintToBuffer( IN CHAR * pchBuffer,
                                IN OUT LPDWORD pcch) const;

    dllexp BOOL UpdatePointers( IN const CHAR * pchOld,
                                IN DWORD cchLen,
                                IN const CHAR * pchNew);

    LIST_ENTRY m_listEntry;
    DWORD      m_nPairs;
    DWORD      m_dwPadding;
    NAME_VALUE_PAIR  m_rgNVP[ MAX_HEADERS_PER_CHUNK];

}; // class NAME_VALUE_CHUNK


class HTTP_HEADERS {

public:

    // ----------------------------------------
    //  General functions
    // ----------------------------------------

    dllexp HTTP_HEADERS(VOID);
    dllexp ~HTTP_HEADERS( VOID);
    dllexp VOID Reset( VOID);
    dllexp BOOL ParseInput( IN const CHAR * pchHeaderBlob,
                            IN DWORD        cchLen,
                            OUT DWORD *     pcbExtraData
                            );

    dllexp VOID InitIterator( OUT HH_ITERATOR * phi)
    {
        phi->dwOrdinal = 0;
        phi->pChunk    = (PVOID)m_ActiveList.Flink;
        phi->dwPair    = 0;

    }  // InitIterator()

    // ----------------------------------------
    //  Fast-Map + Chunks Functions
    // ----------------------------------------
    dllexp BOOL StoreHeader(IN const CHAR * pszHeader, IN DWORD cchHeader,
                            IN const CHAR * pszValue,  IN DWORD cchValue
                            );

    dllexp BOOL StoreHeader(IN const CHAR * pszHeader,
                            IN const CHAR * pszValue
                            );

    // Finds the value in both fast-map and non-fast-map chunks
    dllexp CHAR * FindValue( IN LPCSTR    pszName,
                             OUT LPDWORD  pcchValue = NULL);


    // Cancel an item from the headers
    dllexp VOID CancelHeader( IN LPCSTR    pszName);

    dllexp BOOL NextPair( IN OUT HH_ITERATOR *   phi,
                          OUT NAME_VALUE_PAIR ** ppnp
                          );

    // ----------------------------------------
    //  Fast Map functions
    // ----------------------------------------

    // substitute for GetFastMap()->Store()
    dllexp VOID FastMapStore( IN HTTP_FAST_MAP_HEADERS hfm, IN LPCSTR psz)
    {
        DBG_ASSERT( IsValidHfm( hfm));
        m_rgpszHeaders[ hfm] = psz;
        if ( (DWORD ) hfm >= m_iMaxFastMapHeader) {
            m_iMaxFastMapHeader = (DWORD ) hfm + 1;
        }
    }

    // substitute for GetFastMap()->CheckAndConat()
    dllexp BOOL FastMapStoreWithConcat( IN HTTP_FAST_MAP_HEADERS hfm,
                                        IN LPCSTR pszValue, IN DWORD cchValue);

    // substitute for GetFastMap()->Cancel()
    dllexp VOID FastMapCancel( IN HTTP_FAST_MAP_HEADERS hfm)
    { FastMapStore( hfm, NULL); }

    // substitute for GetFastMap()->QueryValue( hfm)
    dllexp LPCSTR FastMapQueryValue( IN HTTP_FAST_MAP_HEADERS hfm) const
    {
        DBG_ASSERT( IsValidHfm( hfm));
        return ( m_rgpszHeaders[ hfm]);
    }

    // substitute for GetFastMap()->QueryStrValue( hfm)
    dllexp LPCSTR FastMapQueryStrValue( IN HTTP_FAST_MAP_HEADERS hfm) const
    {
        DBG_ASSERT( IsValidHfm( hfm));
        return ( (NULL != m_rgpszHeaders[ hfm]) ?
                 m_rgpszHeaders[ hfm] : (LPCSTR ) &m_chNull);
    }

    dllexp DWORD FastMapMaxIndex( VOID) const
    { return ( m_iMaxFastMapHeader); }

    // ----------------------------------------
    //  Functions on Chunks
    // ----------------------------------------

    // Finds the <header,value> pair in non-fast-map
    dllexp NAME_VALUE_PAIR *
    FindValueInChunks( IN LPCSTR  pszName, IN DWORD cchName);

    dllexp BOOL
    NextPairInChunks( IN OUT HH_ITERATOR *   phi,
                      OUT NAME_VALUE_PAIR ** ppnp
                      );

    // ----------------------------------------
    //  Diagnostics/Debugging functions
    // ----------------------------------------
    dllexp VOID PrintToBuffer( IN CHAR * pchBuffer,
                               IN OUT LPDWORD pcch) const;
    dllexp VOID Print( VOID) const;

private:
    DWORD  m_iMaxFastMapHeader; // index for the highest filled fast-map header
    LPCSTR m_rgpszHeaders[ MAX_HTTP_FAST_MAP_HEADERS];  // fast map headers
    CHAR   m_chNull;           // null string

    CHAR   m_rcInlinedHeader[ DEFAULT_HTTP_HEADER_LEN];
    DWORD  m_cchHeaders;

    // Buffer containing the header-chunks
    DWORD  m_cchBuffHeaders; // # of bytes in the buffer
    BUFFER m_buffHeaders;

    // NYI: Consider using singly-linked list
    LIST_ENTRY m_ActiveList;        // objects of type NAME_VALUE_CHUNK
    LIST_ENTRY m_FreeList;          // objects of type NAME_VALUE_CHUNK


    BOOL MakeRoomInBuffer( IN DWORD cchReqd, IN LPCSTR * ppszVal);
    BOOL UpdatePointers( IN const CHAR * pchOld, IN DWORD cchLen,
                         IN const CHAR * pchNew);

    dllexp BOOL ConcatToHolder( IN LPCSTR * ppsz,
                                IN LPCSTR pszNew,
                                IN DWORD  cchNew
                                );

    // Always adds entry in non-fast-map chunks, with concatenation
    dllexp BOOL
    AddEntryToChunks( IN const CHAR * pszHeader, IN DWORD cchHeader,
                      IN const CHAR * pszValue,  IN DWORD cchValue,
              BOOL fCopyValue = FALSE
                );

    dllexp VOID
    CancelHeaderInChunks( IN LPCSTR pszName, IN DWORD cchName);

    BOOL
    ParseHeaderFirstLine( IN const CHAR * pchFirstLine,
                          IN CHAR * pchScan,
                          IN DWORD cchFirstLine);


public:
    static dllexp  BOOL Initialize( VOID);
    static dllexp  VOID Cleanup( VOID);


#ifdef _PRIVATE_HTTP_HEADERS_TEST
    static dllexp  HTTP_HEADER_MAPPER * QueryHHMapper(void);

#endif // _PRIVATE_HTTP_HEADERS_TEST

    static BOOL
    FindOrdinalForHeader( LPCSTR pszHeader, DWORD cchHeader,
                          LPDWORD pdwOrdinal)
    {  return ( sm_hhm.FindOrdinal( pszHeader, cchHeader,
                                    pdwOrdinal )
                );
    }

private:
    dllexp static HTTP_HEADER_MAPPER     sm_hhm;

}; // class HTTP_HEADERS

typedef HTTP_HEADERS * PHHEADERS;

# endif // _DICT_HXX_

/************************ End of File ***********************/