#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
-#include <curl/curl.h>
#include "urldata.h"
#include "cfilters.h"
#include "curl_trc.h"
-#include "curlx/dynbuf.h"
#include "bufq.h"
+#include "select.h"
#include "vquic/capsule.h"
#include "vquic/cf-capsule.h"
-/* recv buffer: 4 chunks of 16KB = 64KB, enough for large datagrams */
+/* send/recv buffer: 4 chunks of 16KB = 64KB, enough for large datagrams */
#define CAPSULE_RECV_CHUNKS 4
+#define CAPSULE_SEND_CHUNKS 4
#define CAPSULE_CHUNK_SIZE (16 * 1024)
struct cf_capsule_ctx {
struct bufq recvbuf;
- struct cf_call_data call_data;
- unsigned char *pending; /* unsent capsule bytes from partial write */
- size_t pending_len; /* total length of pending buffer */
- size_t pending_offset; /* bytes already sent from pending */
- size_t pending_payload; /* original payload len for pending capsule */
+ struct bufq sendbuf;
};
-static void capsule_cf_destroy(struct Curl_cfilter *cf,
+static void cf_capsule_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_capsule_ctx *ctx = cf->ctx;
(void)data;
if(ctx) {
Curl_bufq_free(&ctx->recvbuf);
- curlx_free(ctx->pending);
+ Curl_bufq_free(&ctx->sendbuf);
curlx_safefree(ctx);
}
}
-static CURLcode capsule_cf_connect(struct Curl_cfilter *cf,
+static CURLcode cf_capsule_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
return CURLE_OK;
}
-static CURLcode capsule_cf_send(struct Curl_cfilter *cf,
+static CURLcode cf_capsule_flush(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_capsule_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ size_t nwritten;
+
+ if(Curl_bufq_is_empty(&ctx->sendbuf))
+ return CURLE_OK;
+
+ result = Curl_cf_send_bufq(cf->next, data, &ctx->sendbuf, NULL, 0,
+ &nwritten);
+ if(result) {
+ if(result == CURLE_AGAIN) {
+ CURL_TRC_CF(data, cf, "flush send buffer(%zu) -> EAGAIN",
+ Curl_bufq_len(&ctx->sendbuf));
+ }
+ return result;
+ }
+ return Curl_bufq_is_empty(&ctx->sendbuf) ? CURLE_OK : CURLE_AGAIN;
+}
+
+static CURLcode cf_capsule_send(struct Curl_cfilter *cf,
struct Curl_easy *data,
const uint8_t *buf, size_t len,
bool eos, size_t *pnwritten)
{
struct cf_capsule_ctx *ctx = cf->ctx;
- struct dynbuf dyn;
- size_t nwritten = 0;
- size_t capsule_len;
- size_t remaining;
CURLcode result;
(void)eos;
*pnwritten = 0;
- if(ctx->pending) {
- /* flush remaining bytes from a partially sent capsule */
- remaining = ctx->pending_len - ctx->pending_offset;
- result = Curl_conn_cf_send(cf->next, data,
- ctx->pending + ctx->pending_offset,
- remaining, FALSE, &nwritten);
- if(result && result != CURLE_AGAIN) {
- curlx_safefree(ctx->pending);
+ if(Curl_bufq_is_full(&ctx->sendbuf)) {
+ result = cf_capsule_flush(cf, data);
+ if(result)
return result;
- }
- ctx->pending_offset += nwritten;
- if(ctx->pending_offset < ctx->pending_len)
- return CURLE_AGAIN;
- /* pending capsule has been fully flushed */
- *pnwritten = ctx->pending_payload;
- curlx_safefree(ctx->pending);
- return CURLE_OK;
}
/* encapsulate new payload into a capsule */
- result = Curl_capsule_encap_udp_datagram(&dyn, buf, len);
- if(result) {
- curlx_dyn_free(&dyn);
+ result = Curl_capsule_encap_udp_datagram(&ctx->sendbuf, buf, len);
+ if(result)
return result;
- }
- capsule_len = curlx_dyn_len(&dyn);
- result = Curl_conn_cf_send(cf->next, data,
- (const uint8_t *)curlx_dyn_ptr(&dyn),
- capsule_len, FALSE, &nwritten);
- if(result && result != CURLE_AGAIN) {
- curlx_dyn_free(&dyn);
- return result;
+ result = cf_capsule_flush(cf, data);
+ if(result == CURLE_AGAIN) {
+ /* Could not send it (or all), report success nevertheless as we
+ * have the payload buffered now and will flush it later. */
+ result = CURLE_OK;
}
- if(nwritten < capsule_len) {
- /* partial or zero write - save unsent capsule bytes as pending */
- remaining = capsule_len - nwritten;
- ctx->pending = curlx_malloc(remaining);
- if(!ctx->pending) {
- curlx_dyn_free(&dyn);
- return CURLE_OUT_OF_MEMORY;
- }
- memcpy(ctx->pending, curlx_dyn_ptr(&dyn) + nwritten, remaining);
- ctx->pending_len = remaining;
- ctx->pending_offset = 0;
- ctx->pending_payload = len;
- curlx_dyn_free(&dyn);
- return CURLE_AGAIN;
- }
-
- /* entire capsule sent */
- curlx_dyn_free(&dyn);
- *pnwritten = len;
- return CURLE_OK;
+ if(!result)
+ *pnwritten = len;
+ return result;
}
-static CURLcode capsule_cf_recv(struct Curl_cfilter *cf,
+static CURLcode cf_capsule_recv(struct Curl_cfilter *cf,
struct Curl_easy *data,
char *buf, size_t len,
size_t *pnread)
return result;
}
-static bool capsule_cf_data_pending(struct Curl_cfilter *cf,
+static bool cf_capsule_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct cf_capsule_ctx *ctx = cf->ctx;
return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
}
+static CURLcode cf_capsule_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ CURLcode result = CURLE_OK;
+
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_FLUSH:
+ result = cf_capsule_flush(cf, data);
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+static CURLcode cf_capsule_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_capsule_ctx *ctx = cf->ctx;
+
+ (void)pres2;
+ switch(query) {
+ case CF_QUERY_NEED_FLUSH: {
+ if(!Curl_bufq_is_empty(&ctx->sendbuf)) {
+ *pres1 = TRUE;
+ return CURLE_OK;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return cf->next ?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+static CURLcode cf_capsule_adjust_pollset(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct easy_pollset *ps)
+{
+ struct cf_capsule_ctx *ctx = cf->ctx;
+
+ if(!Curl_bufq_is_empty(&ctx->sendbuf)) {
+ curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
+ if(sock != CURL_SOCKET_BAD)
+ return Curl_pollset_add_out(data, ps, sock);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode cf_capsule_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+
+ if(!cf->connected || cf->shutdown) {
+ *done = TRUE;
+ }
+ else {
+ result = cf_capsule_flush(cf, data);
+ *done = !result;
+ if(result == CURLE_AGAIN)
+ result = CURLE_OK;
+ }
+ return result;
+}
+
struct Curl_cftype Curl_cft_capsule = {
"CAPSULE",
0,
0,
- capsule_cf_destroy,
- capsule_cf_connect,
- Curl_cf_def_shutdown,
- Curl_cf_def_adjust_pollset,
- capsule_cf_data_pending,
- capsule_cf_send,
- capsule_cf_recv,
- Curl_cf_def_cntrl,
+ cf_capsule_destroy,
+ cf_capsule_connect,
+ cf_capsule_shutdown,
+ cf_capsule_adjust_pollset,
+ cf_capsule_data_pending,
+ cf_capsule_send,
+ cf_capsule_recv,
+ cf_capsule_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
- Curl_cf_def_query,
+ cf_capsule_query,
};
-CURLcode Curl_cf_capsule_create(struct Curl_cfilter **pcf,
- struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode cf_capsule_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn)
{
struct Curl_cfilter *cf = NULL;
struct cf_capsule_ctx *ctx;
Curl_bufq_init2(&ctx->recvbuf, CAPSULE_CHUNK_SIZE, CAPSULE_RECV_CHUNKS,
BUFQ_OPT_SOFT_LIMIT);
+ Curl_bufq_init2(&ctx->sendbuf, CAPSULE_CHUNK_SIZE, CAPSULE_SEND_CHUNKS,
+ BUFQ_OPT_SOFT_LIMIT);
result = Curl_cf_create(&cf, &Curl_cft_capsule, ctx);
*pcf = (!result) ? cf : NULL;
if(result && ctx) {
Curl_bufq_free(&ctx->recvbuf);
+ Curl_bufq_free(&ctx->sendbuf);
curlx_free(ctx);
}
return result;
struct Curl_cfilter *cf;
CURLcode result;
- result = Curl_cf_capsule_create(&cf, data, cf_at->conn);
+ result = cf_capsule_create(&cf, data, cf_at->conn);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
return result;