Cuberite
A lightweight, fast and extensible game server for Minecraft
EnvelopeParser.cpp
Go to the documentation of this file.
1 
2 // EnvelopeParser.cpp
3 
4 // Implements the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME
5 
6 #include "Globals.h"
7 #include "EnvelopeParser.h"
8 
9 
10 
11 
12 
14  m_Callbacks(a_Callbacks),
15  m_IsInHeaders(true)
16 {
17 }
18 
19 
20 
21 
22 
23 size_t cEnvelopeParser::Parse(const char * a_Data, size_t a_Size)
24 {
25  if (!m_IsInHeaders)
26  {
27  return 0;
28  }
29 
30  // Start searching 1 char from the end of the already received data, if available:
31  auto searchStart = m_IncomingData.size();
32  searchStart = (searchStart > 1) ? searchStart - 1 : 0;
33 
34  m_IncomingData.append(a_Data, a_Size);
35 
36  size_t idxCRLF = m_IncomingData.find("\r\n", searchStart);
37  if (idxCRLF == AString::npos)
38  {
39  // Not a complete line yet, all input consumed:
40  return a_Size;
41  }
42 
43  // Parse as many lines as found:
44  size_t Last = 0;
45  do
46  {
47  if (idxCRLF == Last)
48  {
49  // This was the last line of the data. Finish whatever value has been cached and return:
50  NotifyLast();
51  m_IsInHeaders = false;
52  return a_Size - (m_IncomingData.size() - idxCRLF) + 2;
53  }
54  if (!ParseLine(m_IncomingData.c_str() + Last, idxCRLF - Last))
55  {
56  // An error has occurred
57  m_IsInHeaders = false;
58  return AString::npos;
59  }
60  Last = idxCRLF + 2;
61  idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2);
62  } while (idxCRLF != AString::npos);
63  m_IncomingData.erase(0, Last);
64 
65  // Parsed all lines and still expecting more
66  return a_Size;
67 }
68 
69 
70 
71 
72 
74 {
75  m_IsInHeaders = true;
76  m_IncomingData.clear();
77  m_LastKey.clear();
78  m_LastValue.clear();
79 }
80 
81 
82 
83 
84 
86 {
87  if (!m_LastKey.empty())
88  {
90  m_LastKey.clear();
91  }
92  m_LastValue.clear();
93 }
94 
95 
96 
97 
98 
99 bool cEnvelopeParser::ParseLine(const char * a_Data, size_t a_Size)
100 {
101  ASSERT(a_Size > 0);
102  if (a_Data[0] <= ' ')
103  {
104  // This line is a continuation for the previous line
105  if (m_LastKey.empty())
106  {
107  return false;
108  }
109  // Append, including the whitespace in a_Data[0]
110  m_LastValue.append(a_Data, a_Size);
111  return true;
112  }
113 
114  // This is a line with a new key:
115  NotifyLast();
116  for (size_t i = 0; i < a_Size; i++)
117  {
118  if (a_Data[i] == ':')
119  {
120  m_LastKey.assign(a_Data, i);
121  if (a_Size > i + 1)
122  {
123  m_LastValue.assign(a_Data + i + 2, a_Size - i - 2);
124  }
125  else
126  {
127  m_LastValue.clear();
128  }
129  return true;
130  }
131  } // for i - a_Data[]
132 
133  // No colon was found, key-less header??
134  return false;
135 }
136 
137 
138 
139 
#define ASSERT(x)
Definition: Globals.h:276
void Reset(void)
Makes the parser forget everything parsed so far, so that it can be reused for parsing another datast...
cCallbacks & m_Callbacks
Callbacks to call for the various events.
size_t Parse(const char *a_Data, size_t a_Size)
Parses the incoming data.
AString m_LastValue
Holds the last parsed value; used for line-wrapped values.
bool ParseLine(const char *a_Data, size_t a_Size)
Parses one line of header data.
AString m_LastKey
Holds the last parsed key; used for line-wrapped values.
cEnvelopeParser(cCallbacks &a_Callbacks)
bool m_IsInHeaders
Set to true while the parser is still parsing the envelope headers.
AString m_IncomingData
Buffer for the incoming data until it is parsed.
void NotifyLast(void)
Notifies the callback of the key / value stored in m_LastKey / m_LastValue, then erases them.
virtual void OnHeaderLine(const AString &a_Key, const AString &a_Value)=0
Called when a full header line is parsed.