forked from ravinet/mahimahi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
http_message_sequence.hh
153 lines (117 loc) · 4.12 KB
/
http_message_sequence.hh
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef HTTP_MESSAGE_SEQUENCE
#define HTTP_MESSAGE_SEQUENCE
#include <string>
#include <queue>
#include "http_message.hh"
template <class MessageType>
class HTTPMessageSequence
{
private:
class InternalBuffer
{
private:
std::string buffer_ {};
public:
bool have_complete_line( void ) const;
std::string get_and_pop_line( void );
void pop_bytes( const size_t n );
bool empty( void ) const { return buffer_.empty(); }
void append( const std::string & str ) { buffer_.append( str ); }
const std::string & str( void ) const { return buffer_; }
};
/* bytes that haven't been parsed yet */
InternalBuffer buffer_ {};
/* complete messages ready to go */
std::queue< MessageType > complete_messages_ {};
/* one loop through the parser */
/* returns whether to continue */
bool parsing_step( void );
/* what to do to create a new message.
must be implemented by subclass */
virtual void initialize_new_message( void ) = 0;
protected:
/* the current message we're working on */
MessageType message_in_progress_ {};
public:
HTTPMessageSequence() {}
/* must accept all of buf */
void parse( const std::string & buf );
/* getters */
bool empty( void ) const { return complete_messages_.empty(); }
const MessageType & front( void ) const { return complete_messages_.front(); }
/* pop one request */
void pop( void ) { complete_messages_.pop(); }
};
template <class MessageType>
bool HTTPMessageSequence<MessageType>::InternalBuffer::have_complete_line( void ) const
{
size_t first_line_ending = buffer_.find( CRLF );
return first_line_ending != std::string::npos;
}
template <class MessageType>
std::string HTTPMessageSequence<MessageType>::InternalBuffer::get_and_pop_line( void )
{
size_t first_line_ending = buffer_.find( CRLF );
assert( first_line_ending != std::string::npos );
std::string first_line( buffer_.substr( 0, first_line_ending ) );
pop_bytes( first_line_ending + CRLF.size() );
return first_line;
}
template <class MessageType>
void HTTPMessageSequence<MessageType>::InternalBuffer::pop_bytes( const size_t num )
{
assert( buffer_.size() >= num );
buffer_.replace( 0, num, std::string() );
}
template <class MessageType>
bool HTTPMessageSequence<MessageType>::parsing_step( void )
{
switch ( message_in_progress_.state() ) {
case FIRST_LINE_PENDING:
/* do we have a complete line? */
if ( not buffer_.have_complete_line() ) { return false; }
/* supply status line to request/response initialization routine */
initialize_new_message();
message_in_progress_.set_first_line( buffer_.get_and_pop_line() );
return true;
case HEADERS_PENDING:
/* do we have a complete line? */
if ( not buffer_.have_complete_line() ) { return false; }
/* is line blank? */
{
std::string line( buffer_.get_and_pop_line() );
if ( line.empty() ) {
message_in_progress_.done_with_headers();
} else {
message_in_progress_.add_header( line );
}
}
return true;
case BODY_PENDING:
{
size_t bytes_read = message_in_progress_.read_in_body( buffer_.str() );
assert( bytes_read == buffer_.str().size() or message_in_progress_.state() == COMPLETE );
buffer_.pop_bytes( bytes_read );
}
return message_in_progress_.state() == COMPLETE;
case COMPLETE:
complete_messages_.emplace( std::move( message_in_progress_ ) );
message_in_progress_ = MessageType();
return true;
}
assert( false );
return false;
}
template <class MessageType>
void HTTPMessageSequence<MessageType>::parse( const std::string & buf )
{
if ( buf.empty() ) { /* EOF */
message_in_progress_.eof();
}
/* append buf to internal buffer */
buffer_.append( buf );
/* parse as much as we can */
while ( parsing_step() ) {}
}
#endif /* HTTP_MESSAGE_SEQUENCE */