|
1248 | 1248 | } |
1249 | 1249 |
|
1250 | 1250 | rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, |
1251 | | - qpack_dec_stream_id); |
1252 | | - if(rc) { |
1253 | | - failf(data, "error binding HTTP/3 qpack streams: %s", |
1254 | | - ngtcp2_strerror(rc)); |
1255 | | - return CURLE_QUIC_CONNECT_ERROR; |
1256 | | - } |
1257 | | - |
1258 | | - return CURLE_OK; |
1259 | | - } |
1260 | | - |
1261 | | - static ssize_t recv_closed_stream(struct Curl_cfilter *cf, |
1262 | | - struct Curl_easy *data, |
1263 | | - struct h3_stream_ctx *stream, |
1264 | | - CURLcode *err) |
1265 | | - { |
1266 | | - ssize_t nread = -1; |
1267 | | - |
1268 | | - (void)cf; |
1269 | | - if(stream->reset) { |
1270 | | - failf(data, "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->id); |
1271 | | - *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; |
1272 | | - goto out; |
1273 | | - } |
1274 | | - else if(!stream->resp_hds_complete) { |
1275 | | - failf(data, |
1276 | | - "HTTP/3 stream %" FMT_PRId64 " was closed cleanly, but before " |
1277 | | - "getting all response header fields, treated as error", |
1278 | | - stream->id); |
1279 | | - *err = CURLE_HTTP3; |
1280 | | - goto out; |
1281 | | - } |
1282 | | - *err = CURLE_OK; |
1283 | | - nread = 0; |
1284 | | - |
1285 | | - out: |
1286 | | - return nread; |
1287 | | - } |
1288 | | - |
1289 | | - /* incoming data frames on the h3 stream */ |
1290 | | - static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, |
1291 | | - char *buf, size_t blen, size_t *pnread) |
1292 | | - { |
| 1251 | + qpack_dec_stream_id); |
| 1252 | + if(rc) { |
| 1253 | + failf(data, "error binding HTTP/3 qpack streams: %s", |
| 1254 | + ngtcp2_strerror(rc)); |
| 1255 | + return CURLE_QUIC_CONNECT_ERROR; |
| 1256 | + } |
| 1257 | + |
| 1258 | + return CURLE_OK; |
| 1259 | +} |
| 1260 | + |
| 1261 | +static ssize_t recv_closed_stream(struct Curl_cfilter *cf, |
| 1262 | + struct Curl_easy *data, |
| 1263 | + struct h3_stream_ctx *stream, |
| 1264 | + CURLcode *err) |
| 1265 | +{ |
| 1266 | + ssize_t nread = -1; |
| 1267 | + |
| 1268 | + (void)cf; |
| 1269 | + if(stream->reset) { |
| 1270 | + failf(data, "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->id); |
| 1271 | + *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; |
| 1272 | + goto out; |
| 1273 | + } |
| 1274 | + else if(!stream->resp_hds_complete) { |
| 1275 | + failf(data, |
| 1276 | + "HTTP/3 stream %" FMT_PRId64 " was closed cleanly, but before " |
| 1277 | + "getting all response header fields, treated as error", |
| 1278 | + stream->id); |
| 1279 | + *err = CURLE_HTTP3; |
| 1280 | + goto out; |
| 1281 | + } |
| 1282 | + *err = CURLE_OK; |
| 1283 | + nread = 0; |
| 1284 | + |
| 1285 | +out: |
| 1286 | + return nread; |
| 1287 | +} |
| 1288 | + |
| 1289 | +/* incoming data frames on the h3 stream */ |
| 1290 | +static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, |
| 1291 | + char *buf, size_t blen, size_t *pnread) |
| 1292 | +{ |
| 1293 | + struct cf_ngtcp2_ctx *ctx = cf->ctx; |
| 1294 | + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); |
| 1295 | + struct cf_call_data save; |
| 1296 | + struct pkt_io_ctx pktx; |
| 1297 | + CURLcode result = CURLE_OK; |
| 1298 | + |
| 1299 | + (void)ctx; |
| 1300 | + (void)buf; |
| 1301 | + |
| 1302 | + CF_DATA_SAVE(save, cf, data); |
| 1303 | + DEBUGASSERT(cf->connected); |
| 1304 | + DEBUGASSERT(ctx); |
| 1305 | + DEBUGASSERT(ctx->qconn); |
| 1306 | + DEBUGASSERT(ctx->h3conn); |
| 1307 | + *pnread = 0; |
| 1308 | + |
| 1309 | + /* handshake verification failed in callback, do not recv anything */ |
| 1310 | + if(ctx->tls_vrfy_result) |
| 1311 | + return ctx->tls_vrfy_result; |
| 1312 | + |
| 1313 | + pktx_init(&pktx, cf, data); |
| 1314 | + |
| 1315 | + if(!stream || ctx->shutdown_started) { |
| 1316 | + result = CURLE_RECV_ERROR; |
| 1317 | + goto out; |
| 1318 | + } |
| 1319 | + |
| 1320 | + if(cf_progress_ingress(cf, data, &pktx)) { |
| 1321 | + result = CURLE_RECV_ERROR; |
| 1322 | + goto out; |
| 1323 | + } |
| 1324 | + |
| 1325 | + if(stream->xfer_result) { |
| 1326 | + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id); |
| 1327 | + cf_ngtcp2_stream_close(cf, data, stream); |
| 1328 | + result = stream->xfer_result; |
| 1329 | + goto out; |
| 1330 | + } |
| 1331 | + else if(stream->closed) { |
| 1332 | + ssize_t nread = recv_closed_stream(cf, data, stream, &result); |
| 1333 | + if(nread > 0) |
| 1334 | + *pnread = (size_t)nread; |
| 1335 | + goto out; |
| 1336 | + } |
| 1337 | + result = CURLE_AGAIN; |
| 1338 | + |
| 1339 | +out: |
| 1340 | + result = Curl_1st_err(result, cf_progress_egress(cf, data, &pktx)); |
| 1341 | + result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx)); |
| 1342 | + |
| 1343 | + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(blen=%zu) -> %dm, %zu", |
| 1344 | + stream ? stream->id : -1, blen, result, *pnread); |
| 1345 | + CF_DATA_RESTORE(cf, save); |
| 1346 | + return result; |
| 1347 | +} |
| 1348 | + |
| 1349 | +static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, |
| 1350 | + uint64_t datalen, void *user_data, |
| 1351 | + void *stream_user_data) |
| 1352 | +{ |
| 1353 | + struct Curl_cfilter *cf = user_data; |
| 1354 | + struct cf_ngtcp2_ctx *ctx = cf->ctx; |
| 1355 | + struct Curl_easy *data = stream_user_data; |
| 1356 | + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); |
| 1357 | + size_t skiplen; |
| 1358 | + |
| 1359 | + (void)cf; |
| 1360 | + if(!stream) |
| 1361 | + return 0; |
| 1362 | + /* The server acknowledged `datalen` of bytes from our request body. |
| 1363 | + * This is a delta. We have kept this data in `sendbuf` for |
| 1364 | + * re-transmissions and can free it now. */ |
| 1365 | + if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) |
| 1366 | + skiplen = stream->sendbuf_len_in_flight; |
| 1367 | + else |
| 1368 | + skiplen = (size_t)datalen; |
| 1369 | + Curl_bufq_skip(&stream->sendbuf, skiplen); |
| 1370 | + stream->sendbuf_len_in_flight -= skiplen; |
| 1371 | + |
| 1372 | + /* Resume upload processing if we have more data to send */ |
| 1373 | + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { |
| 1374 | + int rv = nghttp3_conn_resume_stream(conn, stream_id); |
| 1375 | + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { |
| 1376 | + return NGHTTP3_ERR_CALLBACK_FAILURE; |
| 1377 | + } |
| 1378 | + } |
| 1379 | + return 0; |
| 1380 | +} |
| 1381 | + |
| 1382 | +static nghttp3_ssize |
| 1383 | +cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, |
| 1384 | + nghttp3_vec *vec, size_t veccnt, |
| 1385 | + uint32_t *pflags, void *user_data, |
| 1386 | + void *stream_user_data) |
| 1387 | +{ |
| 1388 | + struct Curl_cfilter *cf = user_data; |
| 1389 | + struct cf_ngtcp2_ctx *ctx = cf->ctx; |
| 1390 | + struct Curl_easy *data = stream_user_data; |
| 1391 | + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); |
| 1392 | + ssize_t nwritten = 0; |
| 1393 | + size_t nvecs = 0; |
| 1394 | + (void)cf; |
| 1395 | + (void)conn; |
| 1396 | + (void)stream_id; |
| 1397 | + (void)user_data; |
| 1398 | + (void)veccnt; |
| 1399 | + |
| 1400 | + if(!stream) |
| 1401 | + return NGHTTP3_ERR_CALLBACK_FAILURE; |
| 1402 | + /* nghttp3 keeps references to the sendbuf data until it is ACKed |
| 1403 | + * by the server (see `cb_h3_acked_req_body()` for updates). |
| 1404 | + * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` |
| 1405 | + * that we have already passed to nghttp3, but which have not been |
| 1406 | + * ACKed yet. |
| 1407 | + * Any amount beyond `sendbuf_len_in_flight` we need still to pass |
| 1408 | + * to nghttp3. Do that now, if we can. */ |
| 1409 | + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { |
| 1410 | + nvecs = 0; |
| 1411 | + while(nvecs < veccnt && |
| 1412 | + Curl_bufq_peek_at(&stream->sendbuf, |
| 1413 | + stream->sendbuf_len_in_flight, |
| 1414 | + CURL_UNCONST(&vec[nvecs].base), |
| 1415 | + &vec[nvecs].len)) { |
| 1416 | + stream->sendbuf_len_in_flight += vec[nvecs].len; |
| 1417 | + nwritten += vec[nvecs].len; |
| 1418 | + ++nvecs; |
| 1419 | + } |
| 1420 | + DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ |
| 1421 | + } |
| 1422 | + |
| 1423 | + if(nwritten > 0 && stream->upload_left != -1) |
| 1424 | + stream->upload_left -= nwritten; |
| 1425 | + |
| 1426 | + /* When we stopped sending and everything in `sendbuf` is "in flight", |
| 1427 | + * we are at the end of the request body. */ |
| 1428 | + if(stream->upload_left == 0) { |
| 1429 | + *pflags = NGHTTP3_DATA_FLAG_EOF; |
| 1430 | + stream->send_closed = TRUE; |
| 1431 | + } |
| 1432 | + else if(!nwritten) { |
| 1433 | + /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ |
| 1434 | + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN", |
| 1435 | + stream->id); |
| 1436 | + return NGHTTP3_ERR_WOULDBLOCK; |
| 1437 | + } |
| 1438 | + |
| 1439 | + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> " |
| 1440 | + "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", |
| 1441 | + stream->id, (int)nvecs, |
| 1442 | + *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", |
| 1443 | + nwritten, Curl_bufq_len(&stream->sendbuf), |
| 1444 | + stream->upload_left); |
| 1445 | + return (nghttp3_ssize)nvecs; |
| 1446 | +} |
| 1447 | + |
| 1448 | +/* Index where :authority header field will appear in request header |
| 1449 | + field list. */ |
| 1450 | +#define AUTHORITY_DST_IDX 3 |
| 1451 | + |
| 1452 | +static CURLcode h3_stream_open(struct Curl_cfilter *cf, |
| 1453 | + struct Curl_easy *data, |
| 1454 | + const void *buf, size_t len, |
| 1455 | + size_t *pnwritten) |
| 1456 | +{ |
| 1457 | + struct cf_ngtcp2_ctx *ctx = cf->ctx; |
| 1458 | + struct h3_stream_ctx *stream = NULL; |
| 1459 | + int64_t sid; |
| 1460 | + struct dynhds h2_headers; |
| 1461 | + size_t nheader; |
| 1462 | + nghttp3_nv *nva = NULL; |
| 1463 | + int rc = 0; |
| 1464 | + unsigned int i; |
| 1465 | + ssize_t nwritten = -1; |
| 1466 | + nghttp3_data_reader reader; |
| 1467 | + nghttp3_data_reader *preader = NULL; |
| 1468 | + CURLcode result; |
| 1469 | + |
| 1470 | + *pnwritten = 0; |
| 1471 | + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); |
| 1472 | + |
| 1473 | + result = h3_data_setup(cf, data); |
| 1474 | + if(result) |
| 1475 | + goto out; |
| 1476 | + stream = H3_STREAM_CTX(ctx, data); |
| 1477 | + DEBUGASSERT(stream); |
| 1478 | + if(!stream) { |
| 1479 | + result = CURLE_FAILED_INIT; |
| 1480 | + goto out; |
| 1481 | + } |
| 1482 | + |
| 1483 | + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, &result); |
| 1484 | + if(nwritten < 0) |
| 1485 | + goto out; |
| 1486 | + *pnwritten = (size_t)nwritten; |
| 1487 | + |
| 1488 | + /* ... */ |
1293 | 1489 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1294 | 1490 | struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); |
1295 | 1491 | struct cf_call_data save; |
|
0 commit comments