-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detect ping/pong flow-control abilities properly #9642
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,7 @@ struct _CockpitChannelPrivate { | |
gint64 out_window; | ||
|
||
/* Another object giving back-pressure on received data */ | ||
gboolean flow_control; | ||
CockpitFlow *pressure; | ||
gulong pressure_sig; | ||
GQueue *throttled; | ||
|
@@ -217,6 +218,9 @@ process_pong (CockpitChannel *self, | |
{ | ||
gint64 sequence; | ||
|
||
if (!self->priv->flow_control) | ||
return; | ||
|
||
if (!cockpit_json_get_int (pong, "sequence", -1, &sequence)) | ||
{ | ||
g_message ("%s: received invalid \"pong\" \"sequence\" field", self->priv->id); | ||
|
@@ -311,7 +315,6 @@ on_transport_closed (CockpitTransport *transport, | |
cockpit_channel_close (self, problem); | ||
} | ||
|
||
|
||
static void | ||
cockpit_channel_actual_send (CockpitChannel *self, | ||
GBytes *payload, | ||
|
@@ -334,28 +337,31 @@ cockpit_channel_actual_send (CockpitChannel *self, | |
cockpit_transport_send (self->priv->transport, self->priv->id, payload); | ||
|
||
/* A wraparound of our gint64 size? */ | ||
size = g_bytes_get_size (payload); | ||
g_return_if_fail (G_MAXINT64 - size > self->priv->out_sequence); | ||
if (self->priv->flow_control) | ||
{ | ||
size = g_bytes_get_size (payload); | ||
g_return_if_fail (G_MAXINT64 - size > self->priv->out_sequence); | ||
|
||
/* How many bytes have been sent (queued) */ | ||
out_sequence = self->priv->out_sequence + size; | ||
/* How many bytes have been sent (queued) */ | ||
out_sequence = self->priv->out_sequence + size; | ||
|
||
/* Every CHANNEL_FLOW_PING bytes we send a ping */ | ||
if (out_sequence / CHANNEL_FLOW_PING != self->priv->out_sequence / CHANNEL_FLOW_PING) | ||
{ | ||
ping = json_object_new (); | ||
json_object_set_int_member (ping, "sequence", out_sequence); | ||
cockpit_channel_control (self, "ping", ping); | ||
g_debug ("%s: sending ping with sequence: %" G_GINT64_FORMAT, self->priv->id, out_sequence); | ||
json_object_unref (ping); | ||
} | ||
/* Every CHANNEL_FLOW_PING bytes we send a ping */ | ||
if (out_sequence / CHANNEL_FLOW_PING != self->priv->out_sequence / CHANNEL_FLOW_PING) | ||
{ | ||
ping = json_object_new (); | ||
json_object_set_int_member (ping, "sequence", out_sequence); | ||
cockpit_channel_control (self, "ping", ping); | ||
g_debug ("%s: sending ping with sequence: %" G_GINT64_FORMAT, self->priv->id, out_sequence); | ||
json_object_unref (ping); | ||
} | ||
|
||
/* If we've sent more than the window, apply back pressure */ | ||
self->priv->out_sequence = out_sequence; | ||
if (self->priv->out_sequence > self->priv->out_window) | ||
{ | ||
g_debug ("%s: sent too much data without acknowledgement, emitting back pressure", self->priv->id); | ||
cockpit_flow_emit_pressure (COCKPIT_FLOW (self), TRUE); | ||
/* If we've sent more than the window, apply back pressure */ | ||
self->priv->out_sequence = out_sequence; | ||
if (self->priv->out_sequence > self->priv->out_window) | ||
{ | ||
g_debug ("%s: sent too much data without acknowledgement, emitting back pressure", self->priv->id); | ||
cockpit_flow_emit_pressure (COCKPIT_FLOW (self), TRUE); | ||
} | ||
} | ||
|
||
if (validated) | ||
|
@@ -509,6 +515,16 @@ cockpit_channel_real_prepare (CockpitChannel *channel) | |
"channel has invalid \"binary\" option: %s", binary); | ||
} | ||
} | ||
|
||
/* | ||
* The default here, can change from FALSE to TRUE over time once we assume that all | ||
* cockpit-ws participants have been upgraded sufficiently. The default when we're | ||
* on the channel creation side is to handle flow control. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably once we make this decision, we can just revert this patch, in fact. I can't imagine we'll ever have someone sending "{"flow-control": false}". |
||
*/ | ||
if (!cockpit_json_get_bool (options, "flow-control", FALSE, &self->priv->flow_control)) | ||
{ | ||
cockpit_channel_fail (self, "protocol-error", "channel has invalid \"flow-control\" option"); | ||
} | ||
} | ||
|
||
static void | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -395,6 +395,8 @@ cockpit_channel_response_prepare (CockpitChannel *channel) | |
const gchar *payload; | ||
JsonObject *open; | ||
|
||
COCKPIT_CHANNEL_CLASS (cockpit_channel_response_parent_class)->prepare (channel); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add the previously-missing chain-up as a separate commit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
|
||
/* | ||
* Tell the transport to throttle incoming flow on the given channel based on | ||
* output pressure in the web response. | ||
|
@@ -596,6 +598,7 @@ cockpit_channel_response_serve (CockpitWebService *service, | |
|
||
channel = cockpit_web_service_unique_channel (service); | ||
json_object_set_string_member (object, "channel", channel); | ||
json_object_set_boolean_member (object, "flow-control", TRUE); | ||
|
||
if (quoted_etag) | ||
{ | ||
|
@@ -710,6 +713,8 @@ cockpit_channel_response_open (CockpitWebService *service, | |
if (!json_object_has_member (open, "binary")) | ||
json_object_set_string_member (open, "binary", "raw"); | ||
|
||
json_object_set_boolean_member (open, "flow-control", TRUE); | ||
|
||
if (!content_type) | ||
{ | ||
if (!cockpit_web_service_parse_binary (open, &data_type)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to understand -- the binary cockpit-bridge and thus the protocol default to "off" -- but the JavaScript API defaults to on, so that as soon as one uses a newer cockpit-system they get flow control enabled for everything. This would still work with new cockpit-system and old cockpit-bridge as unknown options get ignored. Did I understand that correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's correct.