return total;
}
+/* Try to send a HEADERS frame matching HTX response present in HTX message
+ * <htx> for the H2 stream <h2s>. Returns the number of bytes sent. The caller
+ * must check the stream's status to detect any error which might have happened
+ * subsequently to a successful send. The htx blocks are automatically removed
+ * from the message. The htx message is assumed to be valid since produced from
+ * the internal code, hence it contains a start line, an optional series of
+ * header blocks and an end of header, otherwise an invalid frame could be
+ * emitted and the resulting htx message could be left in an inconsistent state.
+ */
+static size_t h2s_htx_frt_make_resp_headers(struct h2s *h2s, struct htx *htx)
+{
+ struct http_hdr list[MAX_HTTP_HDR];
+ struct h2c *h2c = h2s->h2c;
+ struct htx_blk *blk;
+ struct htx_blk *blk_end;
+ struct buffer outbuf;
+ struct htx_sl *sl;
+ enum htx_blk_type type;
+ int es_now = 0;
+ int ret = 0;
+ int hdr;
+ int idx;
+
+ if (h2c_mux_busy(h2c, h2s)) {
+ h2s->flags |= H2_SF_BLK_MBUSY;
+ return 0;
+ }
+
+ if (!h2_get_buf(h2c, &h2c->mbuf)) {
+ h2c->flags |= H2_CF_MUX_MALLOC;
+ h2s->flags |= H2_SF_BLK_MROOM;
+ return 0;
+ }
+
+ /* determine the first block which must not be deleted, blk_end may
+ * be NULL if all blocks have to be deleted.
+ */
+ idx = htx_get_head(htx);
+ blk_end = NULL;
+ while (idx != -1) {
+ type = htx_get_blk_type(htx_get_blk(htx, idx));
+ idx = htx_get_next(htx, idx);
+ if (type == HTX_BLK_EOH) {
+ if (idx != -1)
+ blk_end = htx_get_blk(htx, idx);
+ break;
+ }
+ }
+
+ /* get the start line, we do have one */
+ blk = htx_get_blk(htx, htx->sl_off);
+ sl = htx_get_blk_ptr(htx, blk);
+ h2s->status = sl->info.res.status;
+
+ /* and the rest of the headers, that we dump starting at header 0 */
+ hdr = 0;
+
+ idx = htx_get_next(htx, htx->sl_off);
+ while ((idx = htx_get_next(htx, idx)) != -1) {
+ blk = htx_get_blk(htx, idx);
+ type = htx_get_blk_type(blk);
+
+ if (type == HTX_BLK_UNUSED)
+ continue;
+
+ if (type != HTX_BLK_HDR)
+ break;
+
+ if (unlikely(hdr >= sizeof(list)/sizeof(list[0]) - 1))
+ goto fail;
+
+ list[hdr].n = htx_get_blk_name(htx, blk);
+ list[hdr].v = htx_get_blk_value(htx, blk);
+
+#if 1
+ {
+ /* FIXME: header names MUST be lower case in H2. For now it's
+ * not granted by HTX so let's force them now.
+ */
+ char *p;
+ for (p = list[hdr].n.ptr; p != list[hdr].n.ptr + list[hdr].n.len; p++)
+ if (unlikely(isupper(*p)))
+ *p = tolower(*p);
+ }
+#endif
+ hdr++;
+ }
+
+ /* marker for end of headers */
+ list[hdr].n = ist("");
+
+ if (h2s->status == 204 || h2s->status == 304) {
+ /* no contents, claim c-len is present and set to zero */
+ es_now = 1;
+ }
+
+ chunk_reset(&outbuf);
+
+ while (1) {
+ outbuf.area = b_tail(&h2c->mbuf);
+ outbuf.size = b_contig_space(&h2c->mbuf);
+ outbuf.data = 0;
+
+ if (outbuf.size >= 9 || !b_space_wraps(&h2c->mbuf))
+ break;
+ realign_again:
+ b_slow_realign(&h2c->mbuf, trash.area, b_data(&h2c->mbuf));
+ }
+
+ if (outbuf.size < 9)
+ goto full;
+
+ /* len: 0x000000 (fill later), type: 1(HEADERS), flags: ENDH=4 */
+ memcpy(outbuf.area, "\x00\x00\x00\x01\x04", 5);
+ write_n32(outbuf.area + 5, h2s->id); // 4 bytes
+ outbuf.data = 9;
+
+ /* encode status, which necessarily is the first one */
+ if (outbuf.data < outbuf.size && h2s->status == 200)
+ outbuf.area[outbuf.data++] = 0x88; // indexed field : idx[08]=(":status", "200")
+ else if (outbuf.data < outbuf.size && h2s->status == 304)
+ outbuf.area[outbuf.data++] = 0x8b; // indexed field : idx[11]=(":status", "304")
+ else if (unlikely(h2s->status < 100 || h2s->status > 999)) {
+ /* this is an unparsable response */
+ goto fail;
+ }
+ else if (unlikely(outbuf.data + 2 + 3 <= outbuf.size)) {
+ /* basic encoding of the status code */
+ outbuf.area[outbuf.data++] = 0x48; // indexed name -- name=":status" (idx 8)
+ outbuf.area[outbuf.data++] = 0x03; // 3 bytes status
+ outbuf.area[outbuf.data++] = '0' + h2s->status / 100;
+ outbuf.area[outbuf.data++] = '0' + h2s->status / 10 % 10;
+ outbuf.area[outbuf.data++] = '0' + h2s->status % 10;
+ }
+ else {
+ if (b_space_wraps(&h2c->mbuf))
+ goto realign_again;
+ goto full;
+ }
+
+ /* encode all headers, stop at empty name */
+ for (hdr = 0; hdr < sizeof(list)/sizeof(list[0]); hdr++) {
+ /* these ones do not exist in H2 and must be dropped. */
+ if (isteq(list[hdr].n, ist("connection")) ||
+ isteq(list[hdr].n, ist("proxy-connection")) ||
+ isteq(list[hdr].n, ist("keep-alive")) ||
+ isteq(list[hdr].n, ist("upgrade")) ||
+ isteq(list[hdr].n, ist("transfer-encoding")))
+ continue;
+
+ if (isteq(list[hdr].n, ist("")))
+ break; // end
+
+ if (!hpack_encode_header(&outbuf, list[hdr].n, list[hdr].v)) {
+ /* output full */
+ if (b_space_wraps(&h2c->mbuf))
+ goto realign_again;
+ goto full;
+ }
+ }
+
+ /* we may need to add END_STREAM.
+ * FIXME: we should also set it when we know for sure that the
+ * content-length is zero as well as on 204/304
+ */
+ if (blk_end && htx_get_blk_type(blk_end) == HTX_BLK_EOM)
+ es_now = 1;
+
+ if (h2s->cs->flags & CS_FL_SHW)
+ es_now = 1;
+
+ /* update the frame's size */
+ h2_set_frame_size(outbuf.area, outbuf.data - 9);
+
+ if (es_now)
+ outbuf.area[4] |= H2_F_HEADERS_END_STREAM;
+
+ /* commit the H2 response */
+ b_add(&h2c->mbuf, outbuf.data);
+ h2s->flags |= H2_SF_HEADERS_SENT;
+
+ /* for now we don't implemented CONTINUATION, so we wait for a
+ * body or directly end in TRL2.
+ */
+ if (es_now) {
+ h2s->flags |= H2_SF_ES_SENT;
+ if (h2s->st == H2_SS_OPEN)
+ h2s->st = H2_SS_HLOC;
+ else
+ h2s_close(h2s);
+ }
+
+ /* OK we could properly deliver the response */
+
+ /* remove all header blocks including the EOH and compute the
+ * corresponding size.
+ *
+ * FIXME: We should remove everything when es_now is set.
+ */
+ ret = 0;
+ idx = htx_get_head(htx);
+ blk = htx_get_blk(htx, idx);
+ while (blk != blk_end) {
+ ret += htx_get_blksz(blk);
+ blk = htx_remove_blk(htx, blk);
+ }
+ end:
+ return ret;
+ full:
+ h2c->flags |= H2_CF_MUX_MFULL;
+ h2s->flags |= H2_SF_BLK_MROOM;
+ ret = 0;
+ goto end;
+ fail:
+ /* unparsable HTX messages, too large ones to be produced in the local
+ * list etc go here (unrecoverable errors).
+ */
+ h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
+ ret = 0;
+ goto end;
+}
+
/* Called from the upper layer, to subscribe to events, such as being able to send */
static int h2_subscribe(struct conn_stream *cs, int event_type, void *param)
{
bsize = htx_get_blksz(blk);
switch (btype) {
+ case HTX_BLK_RES_SL:
+ /* start-line before headers */
+ ret = h2s_htx_frt_make_resp_headers(h2s, htx);
+ if (ret > 0) {
+ total += ret;
+ count -= ret;
+ if (ret < bsize)
+ goto done;
+ }
+ break;
+
default:
htx_remove_blk(htx, blk);
total += bsize;