dodo  0.0.1
A C++ library to create containerized Linux services
httpmessage.hpp
Go to the documentation of this file.
1 /*
2  * This file is part of the dodo library (https://github.com/jmspit/dodo).
3  * Copyright (c) 2019 Jan-Marten Spit.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 /**
20  * @file httpmessage.hpp
21  * Defines the dodo::network::protocol::http::HTTPMessage class.
22  */
23 
24 #ifndef dodo_network_protocol_http_httpmessage_hpp
25 #define dodo_network_protocol_http_httpmessage_hpp
26 
27 #include <common/bytes.hpp>
29 #include <string>
30 
31 namespace dodo {
32 
33  namespace network::protocol::http {
34 
35  /**
36  * Generic HTTPMessage, parent to HTTPRequest and HTTPResponse.
37  * @see https://tools.ietf.org/html/rfc2616#page-15
38  * @remark This class is not thread safe, but does not use any global variables.
39  */
40  class HTTPMessage : public HTTPFragment {
41  public:
42 
43 
44  /**
45  * CR character.
46  */
47  static const char charCR;
48 
49  /**
50  * LF character.
51  */
52  static const char charLF;
53 
54  /**
55  * Space character.
56  */
57  static const char charSP;
58 
59  /**
60  * Horizontal tab character.
61  */
62  static const char charHT;
63 
64  /**
65  * Default constructor.
66  */
67  HTTPMessage() : body_("") {};
68 
69  /**
70  * Add a header to the HTTPMessage. Note that multtiple headers may be specified with the same key
71  * but are supposed to be equavalent to a comma seperated list, so
72  * @code
73  * addHeader( "key", "1" );
74  * addHeader( "key", "2" );
75  * addHeader( "key", "3" );
76  * @endcode
77  * is equivalent to
78  * @code
79  * addHeader( "key", "1,2,3" );
80  * @endcode
81  * and this class transforms the former into the latter automatically.
82  * @param key The header key.
83  * @param value The header value.
84  */
85  void addHeader( const std::string &key, const std::string &value );
86 
87  /**
88  * Replace the value of a header.
89  * @param key the key to replace.
90  * @param value the value to set.
91  */
92  void replaceHeader( const std::string &key, const std::string &value );
93 
94  /**
95  * Return a reference to the headers map.
96  * @return A reference to headers_;
97  */
98  const std::map<std::string,std::string>& getHeaders() const { return headers_; };
99 
100  /**
101  * Return true when this header key exists in this HTTPMessage's headers.
102  * @param header The header key to check for (lowercase).
103  * @return True then the header exists in this HTTPMessage's headers.
104  */
105  bool hasHeader( const std::string &header ) const { return headers_.find(header) != headers_.end(); }
106 
107  /**
108  * Clear all headers.
109  */
110  void clearHeaders() { headers_.clear(); };
111 
112  /**
113  * Get a header value as a string into value.
114  * @param key The header key.
115  * @param value Receives the header value.
116  * @return True if the key was found, false otherwise, in which case value is undefined.
117  */
118  bool getHeaderValue( const std::string &key, std::string &value ) const;
119 
120  /**
121  * Get a header value as an unsigned long into value.
122  * @param key The header key.
123  * @param value Receives the header value.
124  * @return True if the key was found and the value an integer, false otherwise, in which case value is
125  * undefined.
126  */
127  bool getHeaderValue( const std::string &key, unsigned long &value ) const;
128 
129  /**
130  * Return the HTTTMessage body.
131  * @return The HTTPMessage body.
132  */
133  const common::Bytes& getBody() const { return body_; };
134 
135  /**
136  * Set the body.
137  * @param body The body to set.
138  */
139  void setBody( const std::string& body );
140 
141  /**
142  * Send this HTTPMessage to the socket.
143  * @param socket The socket to write to.
144  * @return The commonSystemError that might accour.
145  */
146  virtual common::SystemError send( BaseSocket* socket ) = 0;
147 
148 
149  protected:
150 
151  /**
152  * Parse a header section and update headers_.
153  * @param data The VirtualReadBuffer to read from.
154  * @return The ParseResult.
155  */
156  ParseResult parseHeaders( VirtualReadBuffer &data );
157 
158  /**
159  * Check is the char is a CTL character.
160  * @param c The character to check.
161  * @return true is c is a control character
162  * @see https://tools.ietf.org/html/rfc2616#page-15
163  */
164  static bool isCTL( char c ) { return c <= 31 || c == 127; };
165 
166  /**
167  * Call buffer.next() as long as buffer.get() is whitespace ( charSP or charHT).
168  * @param data The VirtualReadBuffer to read from.
169  * @return A SystemError, like data tata presenting a CR but no LF after it.
170  */
172 
173  /**
174  * Consume a CR LF sequence only when it is there. Unlike parseCRLF this function
175  * will not fail when the CRLF is not found.
176  * @param data The VirtualReadBuffer to read from.
177  * @return peOk or peExpectCRLF or peSystemError. The latter can only be returned when a CR is encountered without
178  * a LF after it.
179  */
180  ParseResult eatCRLF( VirtualReadBuffer& data );
181 
182  /**
183  * Consume a CR LF sequence.
184  * @param data The VirtualReadBuffer to read from.
185  * @return The ParseResult
186  */
187  ParseResult parseCRLF( VirtualReadBuffer& data );
188 
189  /**
190  * Parse a hexadecimal chunk size (a hex value followed by CR LF).
191  * @param data The VirtualReadBuffer to read from.
192  * @param value The value receiving the chunk size.
193  * @return The ParseResult, either peOk, peExpectCRLF or peInvalidChunkHex.
194  */
195  ParseResult parseChunkHex( VirtualReadBuffer& data, unsigned long &value );
196 
197  /**
198  * Parse a header field value.
199  * @param data The VirtualReadBuffer to read from.
200  * @param value The value receiving the field-value.
201  * @return The ParseResult.
202  */
203  ParseResult parseFieldValue( VirtualReadBuffer &data, std::string &value );
204 
205  /**
206  * Parse a token (such as a header field name).
207  * @param buffer The HTTP data source.
208  * @param token The value receiving the token.
209  * @return The ParseResult.
210  */
211  ParseResult parseToken( VirtualReadBuffer& buffer, std::string &token );
212 
213  /**
214  * Parse an unsigned integer.
215  * @param data The VirtualReadBuffer to read from.
216  * @param value The value receiving the unsigned int.
217  * @return The ParseResult.
218  */
219  static ParseResult parseUInt( VirtualReadBuffer &data, unsigned int& value );
220 
221  /**
222  * Parse a chunked body (transfer-encoding: chunked) and resturn it as a single string.
223  * @param data The VirtualReadBuffer to read from.
224  * @return The ParseError.
225  */
226  ParseResult parseChunkedBody(VirtualReadBuffer &data );
227 
228  /**
229  * Parse a body and resturn it as a single string. Note that the content-length header must be present
230  * and specify the correct body length.
231  * @param data The VirtualReadBuffer to read from.
232  * @return The ParseError.
233  */
234  virtual ParseResult parseBody( VirtualReadBuffer &data ) = 0;
235 
236  /**
237  * Return true if the char is a separator.
238  * @param c The char to check.
239  * @return True if c is a separator, false otherwise.
240  * @todo the order of checks can be optimized on character frequency.
241  */
242  static bool isSeparator( char c ) {
243  return c == '(' || c == ')' || c == '<' || c == '>' || c == '@' ||
244  c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' ||
245  c == '/' || c == '[' || c == ']' || c == '?' || c == '=' ||
246  c == '{' || c == '}' || c == charSP || c == charHT;
247  }
248 
249  /**
250  * Return true if the char is whitespace character (charSP or charHT)
251  * @param c The char to check.
252  * @return True if c is whitespace, false otherwise.
253  * @todo the order of checks can be optimized on character frequency.
254  */
255  static bool isSP( char c ) {
256  return c == charSP || c == charHT;
257  }
258 
259  /**
260  * The message headers.
261  */
262  std::map<std::string,std::string> headers_;
263 
264  /**
265  * The message body (if any).
266  */
268 
269  };
270 
271  }
272 
273 }
274 
275 #endif
dodo::network::protocol::http::HTTPMessage::addHeader
void addHeader(const std::string &key, const std::string &value)
Add a header to the HTTPMessage.
Definition: httpmessage.cpp:37
dodo::network::protocol::http::HTTPMessage::parseFieldValue
ParseResult parseFieldValue(VirtualReadBuffer &data, std::string &value)
Parse a header field value.
Definition: httpmessage.cpp:141
dodo::network::protocol::http::HTTPMessage::isCTL
static bool isCTL(char c)
Check is the char is a CTL character.
Definition: httpmessage.hpp:164
dodo::network::protocol::http::HTTPMessage::parseChunkedBody
ParseResult parseChunkedBody(VirtualReadBuffer &data)
Parse a chunked body (transfer-encoding: chunked) and resturn it as a single string.
Definition: httpmessage.cpp:224
dodo::network::protocol::http::HTTPMessage::hasHeader
bool hasHeader(const std::string &header) const
Return true when this header key exists in this HTTPMessage's headers.
Definition: httpmessage.hpp:105
dodo::network::protocol::http::HTTPMessage::charCR
static const char charCR
CR character.
Definition: httpmessage.hpp:47
dodo::network::protocol::http::HTTPMessage::charLF
static const char charLF
LF character.
Definition: httpmessage.hpp:52
dodo::network::protocol::http::HTTPMessage::parseChunkHex
ParseResult parseChunkHex(VirtualReadBuffer &data, unsigned long &value)
Parse a hexadecimal chunk size (a hex value followed by CR LF).
Definition: httpmessage.cpp:125
dodo::network::VirtualReadBuffer
Interface to read individual bytes whilst the implementation can read from an actual source (such as ...
Definition: socketreadbuffer.hpp:44
dodo::network::protocol::http::HTTPMessage::getHeaderValue
bool getHeaderValue(const std::string &key, std::string &value) const
Get a header value as a string into value.
Definition: httpmessage.cpp:74
dodo::network::protocol::http::HTTPMessage::getHeaders
const std::map< std::string, std::string > & getHeaders() const
Return a reference to the headers map.
Definition: httpmessage.hpp:98
dodo::network::protocol::http::HTTPMessage::headers_
std::map< std::string, std::string > headers_
The message headers.
Definition: httpmessage.hpp:262
dodo::common::Bytes
An array of Octets with size elements.
Definition: bytes.hpp:44
dodo::network::protocol::http::HTTPMessage::charHT
static const char charHT
Horizontal tab character.
Definition: httpmessage.hpp:62
dodo::network::protocol::http::HTTPMessage::charSP
static const char charSP
Space character.
Definition: httpmessage.hpp:57
dodo::network::protocol::http::HTTPFragment
Generic HTTP fragment, either a complete (such as HTTPRequest) or incomplete HTTP fragment (ssuch as ...
Definition: httpfragment.hpp:45
dodo::network::protocol::http::HTTPMessage::parseUInt
static ParseResult parseUInt(VirtualReadBuffer &data, unsigned int &value)
Parse an unsigned integer.
Definition: httpmessage.cpp:211
dodo::network::protocol::http::HTTPMessage::getBody
const common::Bytes & getBody() const
Return the HTTTMessage body.
Definition: httpmessage.hpp:133
dodo::network::protocol::http::HTTPMessage::clearHeaders
void clearHeaders()
Clear all headers.
Definition: httpmessage.hpp:110
dodo
A C++ platform interface to lean Linux services tailored for containerized deployment.
Definition: application.hpp:29
dodo::network::protocol::http::HTTPMessage::isSP
static bool isSP(char c)
Return true if the char is whitespace character (charSP or charHT)
Definition: httpmessage.hpp:255
dodo::network::protocol::http::HTTPMessage::replaceHeader
void replaceHeader(const std::string &key, const std::string &value)
Replace the value of a header.
Definition: httpmessage.cpp:244
dodo::network::protocol::http::HTTPMessage::isSeparator
static bool isSeparator(char c)
Return true if the char is a separator.
Definition: httpmessage.hpp:242
dodo::network::protocol::http::HTTPMessage::HTTPMessage
HTTPMessage()
Default constructor.
Definition: httpmessage.hpp:67
httpfragment.hpp
dodo::network::protocol::http::HTTPMessage
Generic HTTPMessage, parent to HTTPRequest and HTTPResponse.
Definition: httpmessage.hpp:40
dodo::network::protocol::http::HTTPMessage::setBody
void setBody(const std::string &body)
Set the body.
Definition: httpmessage.cpp:248
dodo::network::protocol::http::HTTPMessage::parseToken
ParseResult parseToken(VirtualReadBuffer &buffer, std::string &token)
Parse a token (such as a header field name).
Definition: httpmessage.cpp:196
dodo::network::protocol::http::HTTPMessage::eatSpace
static common::SystemError eatSpace(VirtualReadBuffer &data)
Call buffer.next() as long as buffer.get() is whitespace ( charSP or charHT).
Definition: httpmessage.cpp:56
dodo::network::protocol::http::HTTPMessage::parseHeaders
ParseResult parseHeaders(VirtualReadBuffer &data)
Parse a header section and update headers_.
Definition: httpmessage.cpp:83
dodo::common::SystemError
Linux system error primitive to provide a consistent interface to Linux error codes.
Definition: systemerror.hpp:53
dodo::network::protocol::http::HTTPMessage::body_
common::Bytes body_
The message body (if any).
Definition: httpmessage.hpp:267
dodo::network::protocol::http::HTTPMessage::parseBody
virtual ParseResult parseBody(VirtualReadBuffer &data)=0
Parse a body and resturn it as a single string.
dodo::network::protocol::http::HTTPMessage::eatCRLF
ParseResult eatCRLF(VirtualReadBuffer &data)
Consume a CR LF sequence only when it is there.
Definition: httpmessage.cpp:46
bytes.hpp
dodo::network::protocol::http::HTTPMessage::parseCRLF
ParseResult parseCRLF(VirtualReadBuffer &data)
Consume a CR LF sequence.
Definition: httpmessage.cpp:115
dodo::network::protocol::http::HTTPMessage::send
virtual common::SystemError send(BaseSocket *socket)=0
Send this HTTPMessage to the socket.
dodo::network::BaseSocket
Interface to and common implementation of concrete sockets (Socket, TLSSocket).
Definition: basesocket.hpp:36