return 1;
}
-/* marks an error on the connection */
+/* marks an error on the connection. Before settings are sent, we must not send
+ * a GOAWAY frame, and the error state will prevent h2c_send_goaway_error()
+ * from verifying this so we set H2_CF_GOAWAY_FAILED to make sure it will not
+ * even try.
+ */
static inline __maybe_unused void h2c_error(struct h2c *h2c, enum h2_err err)
{
TRACE_POINT(H2_EV_H2C_ERR, h2c->conn, 0, 0, (void *)(long)(err));
h2c->errcode = err;
+ if (h2c->st0 < H2_CS_SETTINGS1)
+ h2c->flags |= H2_CF_GOAWAY_FAILED;
h2c->st0 = H2_CS_ERROR;
}
* the message, it subscribes the requester (either <h2s> or <h2c>) to future
* notifications. It sets H2_CF_GOAWAY_SENT on success, and H2_CF_GOAWAY_FAILED
* on unrecoverable failure. It will not attempt to send one again in this last
- * case so that it is safe to use h2c_error() to report such errors.
+ * case, nor will it send one if settings were not sent (e.g. still waiting for
+ * a preface) so that it is safe to use h2c_error() to report such errors.
*/
static int h2c_send_goaway_error(struct h2c *h2c, struct h2s *h2s)
{
TRACE_ENTER(H2_EV_TX_FRAME|H2_EV_TX_GOAWAY, h2c->conn);
- if (h2c->flags & H2_CF_GOAWAY_FAILED) {
+ if ((h2c->flags & H2_CF_GOAWAY_FAILED) || h2c->st0 < H2_CS_SETTINGS1) {
ret = 1; // claim that it worked
goto out;
}