283 lines
5.7 KiB
C
283 lines
5.7 KiB
C
//-----------------------------------------------------------------------
|
|
// github.com/samisalreadytaken/sqdbg
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
|
|
#ifndef SQDBG_DAP_H
|
|
#define SQDBG_DAP_H
|
|
|
|
#define DAP_HEADER_CONTENTLENGTH "Content-Length"
|
|
#define DAP_HEADER_END "\r\n\r\n"
|
|
#define DAP_HEADER_MAXSIZE ( STRLEN(DAP_HEADER_CONTENTLENGTH ": ") + STRLEN(DAP_HEADER_END) + FMT_UINT32_LEN )
|
|
|
|
inline void DAP_Serialise( CBuffer *buffer )
|
|
{
|
|
Assert( buffer->Size() > 0 && buffer->Size() < INT_MAX );
|
|
|
|
char *mem = buffer->Base();
|
|
int contentSize = buffer->Size() - DAP_HEADER_MAXSIZE;
|
|
int digits = countdigits( contentSize );
|
|
int padding = FMT_UINT32_LEN - digits;
|
|
|
|
int nearest = 10;
|
|
while ( contentSize >= nearest )
|
|
nearest *= 10;
|
|
|
|
contentSize += padding;
|
|
|
|
if ( contentSize >= nearest )
|
|
{
|
|
// Padding between header and content increased content size digits,
|
|
// add padding in the end to match
|
|
padding--;
|
|
digits++;
|
|
buffer->base.Ensure( buffer->Size() + 1 );
|
|
mem[buffer->size++] = ' ';
|
|
}
|
|
|
|
memcpy( mem, DAP_HEADER_CONTENTLENGTH ": ", STRLEN(DAP_HEADER_CONTENTLENGTH ": ") );
|
|
|
|
int idx = STRLEN(DAP_HEADER_CONTENTLENGTH ": ") + digits;
|
|
|
|
for ( int i = idx - 1; contentSize; )
|
|
{
|
|
char c = contentSize % 10;
|
|
contentSize /= 10;
|
|
mem[i--] = '0' + c;
|
|
}
|
|
|
|
memcpy( mem + idx, DAP_HEADER_END, STRLEN(DAP_HEADER_END) );
|
|
idx += STRLEN(DAP_HEADER_END);
|
|
memset( mem + idx, ' ', padding );
|
|
}
|
|
|
|
inline void DAP_Free( CBuffer *buffer )
|
|
{
|
|
buffer->size = 0;
|
|
}
|
|
|
|
static inline int ParseFieldName( const char *pMemEnd, char *pStart )
|
|
{
|
|
char *c = pStart;
|
|
|
|
for (;;)
|
|
{
|
|
if ( IN_RANGE_CHAR( ((unsigned char*)c)[0], 0x20, 0x7E ) )
|
|
{
|
|
if ( c + 1 >= pMemEnd )
|
|
return -1;
|
|
|
|
if ( c[0] == ':' )
|
|
{
|
|
if ( c[1] == ' ' )
|
|
return c - pStart;
|
|
|
|
return 0;
|
|
}
|
|
|
|
c++;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline int ParseFieldValue( const char *pMemEnd, char *pStart )
|
|
{
|
|
char *c = pStart;
|
|
|
|
for (;;)
|
|
{
|
|
if ( c + 1 >= pMemEnd )
|
|
return -1;
|
|
|
|
if ( c[0] == '\n' )
|
|
return 0;
|
|
|
|
if ( c[0] == '\r' && c[1] == '\n' )
|
|
return c - pStart;
|
|
|
|
c++;
|
|
}
|
|
}
|
|
|
|
inline bool DAP_ReadHeader( char **ppMsg, int *pLength )
|
|
{
|
|
char *pMsg = *ppMsg;
|
|
const char *pMemEnd = pMsg + *pLength;
|
|
int nContentLength = 0;
|
|
|
|
for (;;)
|
|
{
|
|
int len = ParseFieldName( pMemEnd, pMsg );
|
|
|
|
if ( len == 0 )
|
|
goto invalid;
|
|
|
|
if ( len == -1 )
|
|
return false;
|
|
|
|
if ( len == (int)STRLEN(DAP_HEADER_CONTENTLENGTH) &&
|
|
!memcmp( pMsg, DAP_HEADER_CONTENTLENGTH, STRLEN(DAP_HEADER_CONTENTLENGTH) ) )
|
|
{
|
|
// Duplicate length field
|
|
if ( nContentLength )
|
|
goto ignore;
|
|
|
|
pMsg += len + 2;
|
|
|
|
for ( char *pStart = pMsg;; )
|
|
{
|
|
if ( pMsg >= pMemEnd )
|
|
return false;
|
|
|
|
if ( IN_RANGE_CHAR( *pMsg, '0', '9' ) )
|
|
{
|
|
nContentLength = nContentLength * 10 + *pMsg - '0';
|
|
pMsg++;
|
|
|
|
if ( pMsg - pStart > (int)FMT_UINT32_LEN )
|
|
goto invalid;
|
|
}
|
|
// Strict - no whitespace allowed
|
|
else
|
|
{
|
|
if ( pMsg + 1 >= pMemEnd )
|
|
return false;
|
|
|
|
if ( pMsg[0] == '\r' && pMsg[1] == '\n' )
|
|
{
|
|
if ( nContentLength <= 0 )
|
|
goto invalid;
|
|
|
|
*pLength = nContentLength;
|
|
pMsg += 2;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
goto invalid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Ignore unknown header fields
|
|
else
|
|
{
|
|
ignore:
|
|
pMsg += len + 2;
|
|
|
|
len = ParseFieldValue( pMemEnd, pMsg );
|
|
|
|
if ( len == 0 )
|
|
goto invalid;
|
|
|
|
if ( len == -1 )
|
|
return false;
|
|
|
|
pMsg += len + 2;
|
|
}
|
|
|
|
if ( pMsg + 1 >= pMemEnd )
|
|
return false;
|
|
|
|
if ( pMsg[0] == '\r' && pMsg[1] == '\n' )
|
|
{
|
|
*ppMsg = pMsg + 2;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
invalid:
|
|
// Signal that the client needs to be dropped
|
|
*pLength = -1;
|
|
*ppMsg = pMsg;
|
|
return true;
|
|
}
|
|
|
|
#ifdef SQDBG_VALIDATE_SENT_MSG
|
|
inline void DAP_Test( CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *scratch, CBuffer *buffer )
|
|
{
|
|
char *pMsg = buffer->Base();
|
|
int nLength = buffer->Size();
|
|
|
|
bool res = DAP_ReadHeader( &pMsg, &nLength );
|
|
Assert( res && nLength < buffer->Size() );
|
|
|
|
if ( res )
|
|
{
|
|
json_table_t table;
|
|
JSONParser parser( scratch, pMsg, nLength, &table );
|
|
|
|
AssertMsg1( !parser.GetError(), "%s", parser.GetError() );
|
|
}
|
|
}
|
|
#else
|
|
#define DAP_Test(...) (void)0
|
|
#endif
|
|
|
|
#define _DAP_INIT_BUF( _buf ) \
|
|
CBufTmpCache _bufcache( (_buf) ); \
|
|
(_buf)->size = DAP_HEADER_MAXSIZE; \
|
|
(void)0
|
|
|
|
#define DAP_START_REQUEST( _seq, _cmd ) \
|
|
{ \
|
|
_DAP_INIT_BUF( &m_SendBuf ); \
|
|
{ \
|
|
wjson_table_t packet( m_SendBuf ); \
|
|
packet.SetInt( "seq", _seq ); \
|
|
packet.SetString( "type", "request" ); \
|
|
packet.SetString( "command", _cmd );
|
|
|
|
#define _DAP_START_RESPONSE( _seq, _cmd, _suc ) \
|
|
if ( IsClientConnected() ) \
|
|
{ \
|
|
_DAP_INIT_BUF( &m_SendBuf ); \
|
|
{ \
|
|
wjson_table_t packet( m_SendBuf ); \
|
|
packet.SetInt( "request_seq", _seq ); \
|
|
packet.SetString( "type", "response" ); \
|
|
packet.SetString( "command", _cmd ); \
|
|
packet.SetBool( "success", _suc );
|
|
|
|
#define DAP_START_RESPONSE( _seq, _cmd ) \
|
|
_DAP_START_RESPONSE( _seq, _cmd, true )
|
|
|
|
#define DAP_ERROR_RESPONSE( _seq, _cmd ) \
|
|
_DAP_START_RESPONSE( _seq, _cmd, false )
|
|
|
|
#define DAP_ERROR_BODY( _id, _fmt ) \
|
|
wjson_table_t body = packet.SetTable( "body" ); \
|
|
wjson_table_t error = body.SetTable( "error" ); \
|
|
error.SetInt( "id", _id ); \
|
|
error.SetString( "format", _fmt ); \
|
|
|
|
#define DAP_START_EVENT( _seq, _ev ) \
|
|
{ \
|
|
_DAP_INIT_BUF( &m_SendBuf ); \
|
|
{ \
|
|
wjson_table_t packet( m_SendBuf ); \
|
|
packet.SetInt( "seq", _seq ); \
|
|
packet.SetString( "type", "event" ); \
|
|
packet.SetString( "event", _ev );
|
|
|
|
#define DAP_SET( _key, _val ) \
|
|
packet.Set( _key, _val );
|
|
|
|
#define DAP_SET_TABLE( _val ) \
|
|
wjson_table_t _val = packet.SetTable( #_val );
|
|
|
|
#define DAP_SEND() \
|
|
} \
|
|
\
|
|
DAP_Serialise( &m_SendBuf ); \
|
|
Send( m_SendBuf.Base(), m_SendBuf.Size() ); \
|
|
DAP_Test( &m_ReadBuf, &m_SendBuf ); \
|
|
DAP_Free( &m_SendBuf ); \
|
|
}
|
|
|
|
#endif // SQDBG_DAP_H
|