summaryrefslogtreecommitdiffstats
path: root/src/HTTP/EnvelopeParser.cpp
blob: 15ab953809aaf944b5dfb6a6bb34036557c7b801 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

// EnvelopeParser.cpp

// Implements the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME

#include "Globals.h"
#include "EnvelopeParser.h"





cEnvelopeParser::cEnvelopeParser(cCallbacks & a_Callbacks) :
	m_Callbacks(a_Callbacks),
	m_IsInHeaders(true)
{
}





size_t cEnvelopeParser::Parse(const char * a_Data, size_t a_Size)
{
	if (!m_IsInHeaders)
	{
		return 0;
	}

	// Start searching 1 char from the end of the already received data, if available:
	auto searchStart = m_IncomingData.size();
	searchStart = (searchStart > 1) ? searchStart - 1 : 0;

	m_IncomingData.append(a_Data, a_Size);

	size_t idxCRLF = m_IncomingData.find("\r\n", searchStart);
	if (idxCRLF == AString::npos)
	{
		// Not a complete line yet, all input consumed:
		return a_Size;
	}

	// Parse as many lines as found:
	size_t Last = 0;
	do
	{
		if (idxCRLF == Last)
		{
			// This was the last line of the data. Finish whatever value has been cached and return:
			NotifyLast();
			m_IsInHeaders = false;
			return a_Size - (m_IncomingData.size() - idxCRLF) + 2;
		}
		if (!ParseLine(m_IncomingData.c_str() + Last, idxCRLF - Last))
		{
			// An error has occurred
			m_IsInHeaders = false;
			return AString::npos;
		}
		Last = idxCRLF + 2;
		idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2);
	} while (idxCRLF != AString::npos);
	m_IncomingData.erase(0, Last);

	// Parsed all lines and still expecting more
	return a_Size;
}





void cEnvelopeParser::Reset(void)
{
	m_IsInHeaders = true;
	m_IncomingData.clear();
	m_LastKey.clear();
	m_LastValue.clear();
}





void cEnvelopeParser::NotifyLast(void)
{
	if (!m_LastKey.empty())
	{
		m_Callbacks.OnHeaderLine(m_LastKey, m_LastValue);
		m_LastKey.clear();
	}
	m_LastValue.clear();
}





bool cEnvelopeParser::ParseLine(const char * a_Data, size_t a_Size)
{
	ASSERT(a_Size > 0);
	if (a_Data[0] <= ' ')
	{
		// This line is a continuation for the previous line
		if (m_LastKey.empty())
		{
			return false;
		}
		// Append, including the whitespace in a_Data[0]
		m_LastValue.append(a_Data, a_Size);
		return true;
	}

	// This is a line with a new key:
	NotifyLast();
	for (size_t i = 0; i < a_Size; i++)
	{
		if (a_Data[i] == ':')
		{
			m_LastKey.assign(a_Data, i);
			if (a_Size > i + 1)
			{
				m_LastValue.assign(a_Data + i + 2, a_Size - i - 2);
			}
			else
			{
				m_LastValue.clear();
			}
			return true;
		}
	}  // for i - a_Data[]

	// No colon was found, key-less header??
	return false;
}