}
if (FLAG_ACK & get_flags())
- return;
-
- if (src_id == HttpCommon::SRC_SERVER && !ssn_data->was_server_settings_received())
- ssn_data->set_server_settings_received();
+ apply_settings();
+ else
+ {
+ if (src_id == HttpCommon::SRC_SERVER && !ssn_data->was_server_settings_received())
+ ssn_data->set_server_settings_received();
- parse_settings_frame();
+ queue_settings();
+ }
}
-void Http2SettingsFrame::parse_settings_frame()
+void Http2SettingsFrame::queue_settings()
{
+ auto& settings_queue = session_data->settings_queue[source_id];
+ // Insert new settings in the queue (duplicating latest queued or current)
+ if (not settings_queue.extend(session_data->connection_settings[source_id]))
+ {
+ session_data->events[source_id]->create_event(EVENT_SETTINGS_QUEUE_OVERFLOW);
+ *session_data->infractions[source_id] += INF_SETTINGS_QUEUE_OVERFLOW;
+ }
+
+ // Update new settings values based on received frame
+ Http2ConnectionSettings& settings = settings_queue.back();
+
int32_t data_pos = 0;
while (data_pos < data.length())
{
session_data->events[source_id]->create_event(EVENT_SETTINGS_FRAME_UNKN_PARAM);
*session_data->infractions[source_id] += INF_SETTINGS_FRAME_UNKN_PARAM;
- continue;
}
-
- if (handle_update(parameter_id, parameter_value))
- session_data->connection_settings[source_id].set_param(parameter_id, parameter_value);
+ else if (parameter_id == SFID_ENABLE_PUSH and parameter_value > 1)
+ {
+ session_data->events[source_id]->create_event(EVENT_BAD_SETTINGS_VALUE);
+ *session_data->infractions[source_id] += INF_BAD_SETTINGS_PUSH_VALUE;
+ }
+ else
+ settings.set_param(parameter_id, parameter_value);
}
}
return !(bad_frame);
}
+void Http2SettingsFrame::apply_settings()
+{
+ // Apply settings to direction opposite to current ACK frame.
+ auto settings_source_id = 1 - source_id;
+ assert(settings_source_id == HttpCommon::SRC_CLIENT || settings_source_id == HttpCommon::SRC_SERVER);
+ auto& settings_queue = session_data->settings_queue[settings_source_id];
+ if (settings_queue.size() == 0)
+ {
+ session_data->events[source_id]->create_event(EVENT_SETTINGS_QUEUE_UNDERFLOW);
+ *session_data->infractions[source_id] += INF_SETTINGS_QUEUE_UNDERFLOW;
+ return;
+ }
+
+ auto& next_settings = settings_queue.front();
+ auto& current_settings = session_data->connection_settings[settings_source_id];
+
+ for (uint16_t parameter_id = SFID_HEADER_TABLE_SIZE; parameter_id <= SFID_MAX_HEADER_LIST_SIZE; ++parameter_id)
+ if (next_settings.get_param(parameter_id) != current_settings.get_param(parameter_id))
+ handle_update(parameter_id, next_settings.get_param(parameter_id));
+
+ current_settings = next_settings;
+ settings_queue.pop();
+}
+
bool Http2SettingsFrame::handle_update(uint16_t id, uint32_t value)
{
switch (id)
{
case SFID_HEADER_TABLE_SIZE:
// Sending a table size parameter informs the receiver the maximum hpack dynamic
- // table size they may use.
- session_data->get_hpack_decoder((HttpCommon::SourceId) (1 - source_id))->
+ // table size they may use. The receiver is the sender of this ack.
+ session_data->get_hpack_decoder((HttpCommon::SourceId) (source_id))->
settings_table_size_update(value);
break;
- case SFID_ENABLE_PUSH:
- // Only values of 0 or 1 are allowed
- if (!(value == 0 or value == 1))
- {
- session_data->events[source_id]->create_event(EVENT_BAD_SETTINGS_VALUE);
- *session_data->infractions[source_id] += INF_BAD_SETTINGS_PUSH_VALUE;
- return false;
- }
- break;
default:
break;
}
if (bad_frame)
fprintf(output, " Error in settings frame.");
else if (FLAG_ACK & get_flags())
- fprintf(output, " ACK");
+ fprintf(output, " ACK, Header Table Size: %d.",
+ session_data->connection_settings[1 - source_id].get_param(SFID_HEADER_TABLE_SIZE));
else
fprintf(output, " Parameters in current frame - %d.", (data.length()/6)) ;
parameters[id - 1] = value;
}
+
+void Http2ConnectionSettingsQueue::pop()
+{
+ assert(size());
+ queue->erase(queue->begin());
+ if (queue->size() == 0)
+ {
+ delete queue;
+ queue = nullptr;
+ }
+}
+
+bool Http2ConnectionSettingsQueue::init(Http2ConnectionSettings& item)
+{
+ queue = new std::vector<Http2ConnectionSettings>();
+ queue->reserve(SETTINGS_QUEUE_MAX);
+ queue->push_back(item);
+ return true;
+}
+
+bool Http2ConnectionSettingsQueue::extend()
+{
+ if (size() == SETTINGS_QUEUE_MAX)
+ // to stay in sync, do an implicit tail drop
+ return false;
+
+ auto& item = back();
+ queue->push_back(item);
+ return true;
+}
const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* ssn_data,
HttpCommon::SourceId src_id, Http2Stream* stream);
- void parse_settings_frame();
+ void queue_settings();
bool sanity_check();
+ void apply_settings();
bool handle_update(uint16_t id, uint32_t value);
uint8_t get_flags_mask() const override
4294967295 // Max header list size
};
};
+
+class Http2ConnectionSettingsQueue
+{
+public:
+ ~Http2ConnectionSettingsQueue() { delete queue; }
+ auto size() { return queue ? queue->size() : 0; }
+ bool extend(Http2ConnectionSettings& item) { return size() ? extend() : init(item); }
+ void pop();
+ auto& front() { assert(size()); return queue->front(); }
+ auto& back() { assert(size()); return queue->back(); }
+
+private:
+ bool init(Http2ConnectionSettings& item);
+ bool extend();
+
+ static const uint8_t SETTINGS_QUEUE_MAX = 6;
+ std::vector<Http2ConnectionSettings>* queue = nullptr;
+};
#endif