CVE-2026-18924: HTTP/2 server push UAF
What the report claims
A use-after-free in libcurl's HTTP/2 server push handling occurs when a pushed stream's handle lacks a shared connection pool, causing the connection to be freed while still linked in the parent's cpool.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2026-18924 HTTP/2 server push UAF
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The available evidence does not settle the report. A maintainer should investigate.
See the withdrawn model reasoning
The following reasoning failed the citation check and does not support the final finding.
- All six claims are directly supported by the provided source code at the cited lines.
- The code path is complete: HTTP/2 push creates a handle via h2_duphandle → curl_easy_duphandle (share not copied) → pushed handle gets parent's connection from share->cpool → cpool_get_instance returns wrong pool (multi->cpool) → cpool_remove_conn fails to unlink → connection freed while still in share->cpool → later share cleanup reads freed memory via Curl_node_elem.
- The reproducer demonstrates the issue on both curl 8.21.0 and master (2112f185c) with ASan confirming heap-use-after-free.
- This is a use-after-free (CWE-416), not a NULL dereference, and the project policy does not exclude use-after-free from security issues.
- An attacker controlling an HTTP/2 server can trigger this by sending PUSH_PROMISE followed by stream termination (GOAWAY, RST_STREAM, timeout, etc.).
How this analysis ran 6 claims · 14 citations verified
Evidence check: judge quoted an external source that does not say that (https://sourceware.org/pipermail/cygwin/2022-October/252385.html)
Checked against curl master on 2026-08-04 (69e8278149ce). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Inspect the withdrawn draft reply
This draft belongs to the withdrawn judgment. It may contain unsupported claims.
Thank you for the detailed report and reproducer. The use-after-free in HTTP/2 server push with shared connection pools is confirmed: curl_easy_duphandle does not copy the share pointer (lib/easy.c:978,1010), causing pushed handles to use the wrong connection pool (lib/conncache.c:256-267), which leaves the connection linked in share->cpool after free (lib/conncache.c:169,178) and leads to a read of freed memory during share cleanup (lib/llist.c:237). This is a valid security vulnerability (CWE-416). We will work on a fix propagating the parent's share to the pushed handle.
Inspect all claims and source code 6 claims · 27 code excerpts
This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.
Report claims
curl_easy_duphandle() does not copy the share handle, leaving pushed handles with share == NULL
Read the reasoning
curl_easy_duphandle uses calloc (zeroing share) and dupset does not copy the share field, leaving the cloned handle with share == NULL.
h2_duphandle() calls curl_easy_duphandle() which creates a handle with share == NULL
Read the reasoning
h2_duphandle directly calls curl_easy_duphandle which produces a handle with share == NULL per C1.
The pushed handle is attached to the parent's connection that resides in share->cpool
Read the reasoning
The pushed handle receives the parent's connection (cf->conn) which belongs to share->cpool.
cpool_get_instance() returns multi->cpool for the pushed handle instead of share->cpool
Read the reasoning
For the pushed handle, data->share is NULL, so cpool_get_instance returns multi->cpool instead of share->cpool.
cpool_remove_conn() fails to find the connection because it is looked up in the wrong pool, leaving bits.in_cpool TRUE
Read the reasoning
cpool_remove_conn is called with multi->cpool but the connection is in share->cpool's bundle, so the lookup fails and in_cpool remains TRUE.
The freed connectdata remains linked in share->cpool and is later read by Curl_node_elem() causing use-after-free
Read the reasoning
The freed connection remains linked in share->cpool (in_cpool=TRUE). When share is cleaned up, Curl_cpool_destroy walks the list and Curl_node_elem dereferences the freed memory.
What the report says about other projects
confirmed the clone is "created as if CURLOPT_SHARE was set to NULL"
“the clone is "created as if CURLOPT_SHARE was set to NULL"” source
Read the original report (excerpt)
Source code
| 674 | mostly to detect rubbish input fast(er). Also empty header name |
| 675 | is rubbish too. We have to allow ":" at the beginning of |
| 676 | the header, but header == ":" must be rejected. If we have ':' in |
| 677 | the middle of header, it could be matched in middle of the value, |
| 678 | this is because we do prefix match.*/ |
| 679 | if(!h || !GOOD_EASY_HANDLE(h->data) || !name || !name[0] || |
| 680 | !strcmp(name, ":") || strchr(name + 1, ':')) |
| 681 | return NULL; |
| 682 | |
| 683 | stream = h->stream; |
| 684 | if(!stream) |
| 685 | return NULL; |
| 686 | |
| 687 | len = strlen(name); |
| 688 | for(i = 0; i < stream->push_headers_used; i++) { |
| 689 | if(!strncmp(name, stream->push_headers[i], len)) { |
| 690 | /* sub-match, make sure that it is followed by a colon */ |
| 691 | if(stream->push_headers[i][len] != ':') |
| 692 | continue; |
| 693 | return &stream->push_headers[i][len + 1]; |
| 694 | } |
| 695 | } |
| 696 | return NULL; |
| 697 | } |
| 698 | |
| 699 | static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, |
| 700 | struct Curl_easy *data) |
| 701 | { |
| 702 | struct Curl_easy *second = curl_easy_duphandle(data); |
| C2: h2_duphandle calls curl_easy_duphandle(data) and returns the result | |
| 703 | if(second) { |
| 704 | struct h2_stream_ctx *second_stream; |
| 705 | http2_data_setup(cf, second, &second_stream); |
| 706 | second->state.priority.weight = data->state.priority.weight; |
| 707 | } |
| 708 | return second; |
| 709 | } |
| 710 | |
| 711 | static int set_transfer_url(struct Curl_easy *newhandle, |
| 712 | struct curl_pushheaders *hp, |
| 713 | struct Curl_easy *data) |
| 714 | { |
| 715 | const char *v; |
| 716 | CURLUcode uc; |
| 717 | char *url = NULL; |
| 718 | int rc = 0; |
| 719 | CURLU *u = curl_url(); |
| 720 | |
| 721 | if(!u) |
| 722 | return 5; |
| 723 | |
| 724 | v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME); |
| 945 | if(result) |
| 946 | return result; |
| 947 | } |
| 948 | #endif |
| 949 | |
| 950 | if(src->set.resolve) |
| 951 | dst->state.resolve = dst->set.resolve; |
| 952 | |
| 953 | return result; |
| 954 | } |
| 955 | |
| 956 | static void dupeasy_meta_freeentry(void *p) |
| 957 | { |
| 958 | (void)p; |
| 959 | /* Always FALSE. Cannot use a 0 assert here since compilers |
| 960 | * are not in agreement if they then want a NORETURN attribute or |
| 961 | * not. *sigh* */ |
| 962 | DEBUGASSERT(!p); |
| 963 | } |
| 964 | |
| 965 | /* |
| 966 | * curl_easy_duphandle() is an external interface to allow duplication of a |
| 967 | * given input easy handle. The returned handle will be a new working handle |
| 968 | * with all options set exactly as the input source handle. |
| 969 | */ |
| 970 | CURL *curl_easy_duphandle(CURL *curl) |
| 971 | { |
| 972 | struct Curl_eapi_guard guard; |
| 973 | struct Curl_easy *outcurl = NULL; |
| 974 | |
| 975 | if(CURL_EAPI_ENTER(&guard, curl, easy_duphandle, NULL)) { |
| 976 | struct Curl_easy *data = curl; |
| 977 | |
| 978 | outcurl = curlx_calloc(1, sizeof(struct Curl_easy)); |
| C1: curl_easy_duphandle allocates new handle with curlx_calloc, zeroing all fields including share | |
| 979 | if(!outcurl) |
| 980 | goto fail; |
| 981 | |
| 982 | /* |
| 983 | * We setup a few buffers we need. We should probably make them |
| 984 | * get setup on-demand in the code, as that would probably decrease |
| 985 | * the likeliness of us forgetting to init a buffer here in the future. |
| 986 | */ |
| 987 | outcurl->set.buffer_size = data->set.buffer_size; |
| 988 | |
| 989 | Curl_hash_init(&outcurl->meta_hash, 23, |
| 990 | Curl_hash_str, curlx_str_key_compare, |
| 991 | dupeasy_meta_freeentry); |
| 992 | curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); |
| 993 | Curl_bufref_init(&outcurl->state.url); |
| 994 | Curl_bufref_init(&outcurl->state.referer); |
| 995 | Curl_netrc_init(&outcurl->state.netrc); |
| 996 | |
| 997 | /* the connection pool is setup on demand */ |
| 998 | outcurl->state.lastconnect_id = -1; |
| 999 | outcurl->state.recent_conn_id = -1; |
| 1000 | outcurl->id = -1; |
| 1001 | outcurl->mid = UINT32_MAX; |
| 1002 | outcurl->master_mid = UINT32_MAX; |
| 1003 | |
| 1004 | #ifndef CURL_DISABLE_HTTP |
| 1005 | Curl_llist_init(&outcurl->state.httphdrs, NULL); |
| 1006 | #endif |
| 1007 | Curl_initinfo(outcurl); |
| 1008 | |
| 1009 | /* copy all userdefined values */ |
| 1010 | if(dupset(outcurl, data)) |
| C1: dupset copies user options but does not copy the share pointer | |
| 1011 | goto fail; |
| 1012 | |
| 1013 | outcurl->progress.hide = data->progress.hide; |
| 1014 | outcurl->progress.callback = data->progress.callback; |
| 1015 | |
| 1016 | #ifndef CURL_DISABLE_COOKIES |
| 1017 | outcurl->state.cookielist = NULL; |
| 1018 | if(data->cookies && data->state.cookie_engine) { |
| 1019 | /* If cookies are enabled in the parent handle, we enable them |
| 1020 | in the clone as well! */ |
| 1021 | outcurl->cookies = Curl_cookie_init(); |
| 1022 | if(!outcurl->cookies) |
| 1023 | goto fail; |
| 1024 | outcurl->state.cookie_engine = TRUE; |
| 1025 | } |
| 1026 | |
| 1027 | if(data->state.cookielist) { |
| 1028 | outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); |
| 1029 | if(!outcurl->state.cookielist) |
| 1030 | goto fail; |
| 1031 | } |
| 1032 | #endif |
| 1033 | |
| 1034 | if(Curl_bufref_ptr(&data->state.url)) { |
| 1035 | Curl_bufref_set(&outcurl->state.url, |
| 1036 | Curl_bufref_dup(&data->state.url), 0, |
| 1037 | curl_free); |
| 1038 | if(!Curl_bufref_ptr(&outcurl->state.url)) |
| 1039 | goto fail; |
| 1040 | } |
| 1041 | if(Curl_bufref_ptr(&data->state.referer)) { |
| 1042 | Curl_bufref_set(&outcurl->state.referer, |
| 1043 | Curl_bufref_dup(&data->state.referer), 0, |
| 1044 | curl_free); |
| 1045 | if(!Curl_bufref_ptr(&outcurl->state.referer)) |
| 1046 | goto fail; |
| 1047 | } |
| 1048 | |
| 1049 | /* Reinitialize an SSL engine for the new handle |
| 1050 | * note: the engine name has already been copied by dupset */ |
| 1051 | if(outcurl->set.str[STRING_SSL_ENGINE]) { |
| 1052 | if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) |
| 1053 | goto fail; |
| 1054 | } |
| 1055 | |
| 1056 | #ifndef CURL_DISABLE_ALTSVC |
| 1057 | if(data->asi) { |
| 1058 | outcurl->asi = Curl_altsvc_init(); |
| 1059 | if(!outcurl->asi) |
| 1060 | goto fail; |
| 1061 | if(outcurl->set.str[STRING_ALTSVC]) |
| 1062 | (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]); |
| 1063 | } |
| 1064 | #endif |
| 1065 | #ifndef CURL_DISABLE_HSTS |
| 1066 | if(data->hsts) { |
| 1067 | outcurl->hsts = Curl_hsts_init(); |
| 1068 | if(!outcurl->hsts) |
| 1069 | goto fail; |
| 1070 | if(outcurl->set.str[STRING_HSTS]) |
| 1071 | (void)Curl_hsts_loadfile(outcurl, |
| 1072 | outcurl->hsts, outcurl->set.str[STRING_HSTS]); |
| 1073 | (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); |
| 1074 | |
| 1075 | /* Copy entries learned at runtime. (E.g. Strict-Transport-Security |
| 1076 | headers.) */ |
| 1077 | if(Curl_hsts_copy(outcurl->hsts, data->hsts)) |
| 1078 | goto fail; |
| 1079 | } |
| 1080 | #endif |
| 1081 | |
| 1082 | /* we reach this point and thus we are OK */ |
| 1083 | outcurl->magic = CURLEASY_MAGIC_NUMBER; |
| 1084 | } |
| 1085 | CURL_EAPI_LEAVE(&guard); |
| 1086 | return outcurl; |
| 1087 | |
| 1088 | fail: |
| 1089 | if(outcurl) { |
| 1090 | #ifndef CURL_DISABLE_COOKIES |
| 1091 | curlx_free(outcurl->cookies); |
| 1092 | #endif |
| 1093 | curlx_dyn_free(&outcurl->state.headerb); |
| 1094 | Curl_altsvc_cleanup(&outcurl->asi); |
| 1095 | Curl_hsts_cleanup(&outcurl->hsts); |
| 1096 | Curl_freeset(outcurl); |
| 1097 | curlx_free(outcurl); |
| 1098 | } |
| 1099 | |
| 1100 | CURL_EAPI_LEAVE(&guard); |
| 1101 | return NULL; |
| 1102 | } |
| 231 | void Curl_cpool_destroy(struct cpool *cpool) |
| 232 | { |
| 233 | if(cpool && cpool->initialized && cpool->idata) { |
| 234 | struct connectdata *conn; |
| 235 | struct Curl_sigpipe_ctx pipe_ctx; |
| 236 | |
| 237 | CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections", |
| 238 | cpool->share ? "[SHARE] " : "", cpool->num_conn); |
| 239 | /* Move all connections to the shutdown list */ |
| 240 | sigpipe_init(&pipe_ctx); |
| 241 | CPOOL_LOCK(cpool, cpool->idata); |
| 242 | conn = cpool_get_first(cpool); |
| C6: Curl_cpool_destroy calls cpool_get_first which calls Curl_node_elem on list nodes | |
| 243 | if(conn) |
| 244 | sigpipe_apply(cpool->idata, &pipe_ctx); |
| 245 | while(conn) { |
| 246 | cpool_remove_conn(cpool, conn); |
| 247 | cpool_discard_conn(cpool, cpool->idata, conn, FALSE); |
| 248 | conn = cpool_get_first(cpool); |
| 249 | } |
| 250 | CPOOL_UNLOCK(cpool, cpool->idata); |
| 251 | sigpipe_restore(&pipe_ctx); |
| 252 | Curl_hash_destroy(&cpool->dest2bundle); |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | static struct cpool *cpool_get_instance(struct Curl_easy *data) |
| C4: cpool_get_instance checks CURL_SHARE_KEEP_CONNECT(data->share) first, then falls back to multi->cpool | |
| 257 | { |
| 258 | if(data) { |
| 259 | if(CURL_SHARE_KEEP_CONNECT(data->share)) |
| 260 | return &data->share->cpool; |
| 261 | else if(data->multi_easy) |
| 262 | return &data->multi_easy->cpool; |
| 263 | else if(data->multi) |
| 264 | return &data->multi->cpool; |
| 265 | } |
| 266 | return NULL; |
| 267 | } |
| 268 | |
| 269 | void Curl_cpool_xfer_init(struct Curl_easy *data) |
| 270 | { |
| 271 | struct cpool *cpool = cpool_get_instance(data); |
| 272 | |
| 273 | DEBUGASSERT(cpool); |
| 274 | if(cpool) { |
| 275 | CPOOL_LOCK(cpool, data); |
| 276 | /* the identifier inside the connection cache */ |
| 277 | data->id = cpool->next_easy_id++; |
| 278 | if(cpool->next_easy_id <= 0) |
| 279 | cpool->next_easy_id = 0; |
| 280 | data->state.lastconnect_id = -1; |
| 281 |
| 129 | static struct connectdata *cpool_get_first(struct cpool *cpool) |
| 130 | { |
| 131 | struct Curl_hash_iterator iter; |
| 132 | struct Curl_hash_element *he; |
| 133 | struct cpool_bundle *bundle; |
| 134 | struct Curl_llist_node *conn_node; |
| 135 | |
| 136 | Curl_hash_start_iterate(&cpool->dest2bundle, &iter); |
| 137 | for(he = Curl_hash_next_element(&iter); he; |
| 138 | he = Curl_hash_next_element(&iter)) { |
| 139 | bundle = he->ptr; |
| 140 | conn_node = Curl_llist_head(&bundle->conns); |
| 141 | if(conn_node) |
| 142 | return Curl_node_elem(conn_node); |
| 143 | } |
| 144 | return NULL; |
| 145 | } |
| 146 | |
| 147 | static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool, |
| 148 | struct connectdata *conn) |
| 149 | { |
| 150 | return Curl_hash_pick(&cpool->dest2bundle, |
| 151 | conn->destination, strlen(conn->destination) + 1); |
| 152 | } |
| 153 | |
| 154 | static void cpool_remove_bundle(struct cpool *cpool, |
| 155 | struct cpool_bundle *bundle) |
| 156 | { |
| 157 | if(!cpool) |
| 158 | return; |
| 159 | Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len); |
| 160 | } |
| 161 | |
| 162 | static void cpool_remove_conn(struct cpool *cpool, |
| 163 | struct connectdata *conn) |
| 164 | { |
| 165 | struct Curl_llist *list = Curl_node_llist(&conn->cpool_node); |
| 166 | DEBUGASSERT(cpool); |
| 167 | if(list) { |
| 168 | /* The connection is certainly in the pool, but where? */ |
| 169 | struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn); |
| C5: cpool_find_bundle(cpool, conn) looks up connection in the given pool's hash table | |
| 170 | if(bundle && (list == &bundle->conns)) { |
| 171 | cpool_bundle_remove(bundle, conn); |
| 172 | if(!Curl_llist_count(&bundle->conns)) |
| 173 | cpool_remove_bundle(cpool, bundle); |
| 174 | conn->bits.in_cpool = FALSE; |
| 175 | cpool->num_conn--; |
| 176 | } |
| 177 | else { |
| 178 | /* Should have been in the bundle list */ |
| C5: When bundle not found or list mismatch, DEBUGASSERT(NULL) triggers and conn->bits.in_cpool stays TRUE | |
| 179 | DEBUGASSERT(NULL); |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | static void cpool_discard_conn(struct cpool *cpool, |
| 185 | struct Curl_easy *data, |
| 186 | struct connectdata *conn, |
| 187 | bool aborted) |
| 188 | { |
| 189 | bool done = FALSE; |
| 190 | |
| 191 | DEBUGASSERT(data); |
| 192 | DEBUGASSERT(!data->conn); |
| 193 | DEBUGASSERT(cpool); |
| 194 | DEBUGASSERT(!conn->bits.in_cpool); |
| 195 | |
| 196 | /* |
| 197 | * If this connection is not marked to force-close, leave it open if there |
| 198 | * are other users of it |
| 199 | */ |
| 200 | if(CONN_INUSE(conn) && !aborted) { |
| 201 | CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T |
| 202 | " still in use by %u transfers", conn->connection_id, |
| 203 | conn->attached_xfers); |
| 204 | return; |
| 205 | } |
| 206 | |
| 207 | /* treat the connection as aborted in CONNECT_ONLY situations, we do |
| 208 | * not know what the APP did with it. */ |
| 209 | if(conn->bits.connect_only) |
| 210 | aborted = TRUE; |
| 211 | conn->bits.aborted = aborted; |
| 212 | |
| 213 | /* We do not shutdown dead connections. The term 'dead' can be misleading |
| 214 | * here, as we also mark errored connections/transfers as 'dead'. |
| 215 | * If we do a shutdown for an aborted transfer, the server might think |
| 216 | * it was successful otherwise (for example an ftps: upload). This is |
| 217 | * not what we want. */ |
| 218 | if(aborted) |
| 219 | done = TRUE; |
| 220 | if(!done) { |
| 221 | /* Attempt to shutdown the connection right away. */ |
| 222 | Curl_cshutdn_run_once(cpool->idata, conn, &done); |
| 223 | } |
| 224 | |
| 225 | if(done || !data->multi) |
| 226 | Curl_cshutdn_terminate(cpool->idata, conn, FALSE); |
| C6: cpool_discard_conn calls Curl_cshutdn_terminate which frees the connectdata | |
| 227 | else |
| 228 | Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn); |
| 229 | } |
| 212 | NULL |
| 213 | |
| 214 | @unittest 1300 */ |
| 215 | UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list); |
| 216 | UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list) |
| 217 | { |
| 218 | DEBUGASSERT(list); |
| 219 | DEBUGASSERT(list->_init == LLISTINIT); |
| 220 | return VERIFYNODE(list->_tail); |
| 221 | } |
| 222 | #endif |
| 223 | |
| 224 | /* Curl_llist_count() returns a size_t the number of nodes in the list */ |
| 225 | size_t Curl_llist_count(const struct Curl_llist *list) |
| 226 | { |
| 227 | DEBUGASSERT(list); |
| 228 | DEBUGASSERT(list->_init == LLISTINIT); |
| 229 | return list->_size; |
| 230 | } |
| 231 | |
| 232 | /* Curl_node_elem() returns the custom data from a Curl_llist_node */ |
| 233 | void *Curl_node_elem(const struct Curl_llist_node *n) |
| 234 | { |
| 235 | DEBUGASSERT(n); |
| 236 | DEBUGASSERT(n->_init == NODEINIT); |
| 237 | return n->_ptr; |
| C6: Curl_node_elem reads n->_ptr from the freed connectdata struct | |
| 238 | } |
| 239 | |
| 240 | /* Curl_node_next() returns the next element in a list from a given |
| 241 | Curl_llist_node */ |
| 242 | struct Curl_llist_node *Curl_node_next(const struct Curl_llist_node *n) |
| 243 | { |
| 244 | DEBUGASSERT(n); |
| 245 | DEBUGASSERT(n->_init == NODEINIT); |
| 246 | return VERIFYNODE(n->_next); |
| 247 | } |
| 248 | |
| 249 | #ifdef UNITTESTS |
| 250 | /* llist_node_prev() returns the previous element in a list from a given |
| 251 | Curl_llist_node |
| 252 | |
| 253 | @unittest 1300 */ |
| 254 | UNITTEST struct Curl_llist_node *llist_node_prev( |
| 255 | const struct Curl_llist_node *n); |
| 256 | UNITTEST struct Curl_llist_node *llist_node_prev( |
| 257 | const struct Curl_llist_node *n) |
| 258 | { |
| 259 | DEBUGASSERT(n); |
| 260 | DEBUGASSERT(n->_init == NODEINIT); |
| 261 | return VERIFYNODE(n->_prev); |
| 262 | } |
| 263 | #endif |
| 264 | |
| 265 | struct Curl_llist *Curl_node_llist(const struct Curl_llist_node *n) |
| 266 | { |
| 267 | DEBUGASSERT(n); |
| 268 | DEBUGASSERT(!n->_list || n->_init == NODEINIT); |
| 269 | return n->_list; |
| 270 | } |
| 121 | void Curl_cshutdn_terminate(struct Curl_easy *data, |
| 122 | struct connectdata *conn, |
| 123 | bool do_shutdown) |
| 124 | { |
| 125 | struct Curl_easy *admin = data; |
| 126 | bool done; |
| 127 | |
| 128 | /* there must be a connection to close */ |
| 129 | DEBUGASSERT(conn); |
| 130 | /* it must be removed from the connection pool */ |
| 131 | DEBUGASSERT(!conn->bits.in_cpool); |
| 132 | /* the transfer must be detached from the connection */ |
| 133 | DEBUGASSERT(data && !data->conn); |
| 134 | |
| 135 | /* If we can obtain an internal admin handle, use that to attach |
| 136 | * and terminate the connection. Some protocol will try to mess with |
| 137 | * `data` during shutdown and we do not want that with a `data` from |
| 138 | * the application. */ |
| 139 | if(data->multi && data->multi->admin) |
| 140 | admin = data->multi->admin; |
| 141 | |
| 142 | Curl_attach_connection(admin, conn); |
| 143 | |
| 144 | cshutdn_run_conn_handler(admin, conn); |
| 145 | if(do_shutdown) { |
| 146 | /* Make a last attempt to shutdown handlers and filters, if |
| 147 | * not done so already. */ |
| 148 | cshutdn_run_once(admin, conn, &done); |
| 149 | } |
| 150 | CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T, |
| 151 | conn->bits.shutdown_filters ? "" : "force ", |
| 152 | conn->connection_id); |
| 153 | Curl_conn_cf_discard_all(admin, conn, SECONDARYSOCKET); |
| 154 | Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET); |
| 155 | Curl_detach_connection(admin); |
| 156 | |
| 157 | if(data->multi) |
| 158 | Curl_multi_ev_conn_done(data->multi, data, conn); |
| 159 | Curl_conn_free(admin, conn); |
| C6: Curl_cshutdn_terminate calls Curl_conn_free to free the connection | |
| 160 | |
| 161 | if(data->multi) { |
| 162 | CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged"); |
| 163 | Curl_multi_connchanged(data->multi); |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn, |
| 168 | struct Curl_easy *data, |
| 169 | const char *destination) |
| 170 | { |
| 171 | struct Curl_llist_node *e; |
| 172 | struct connectdata *conn; |
| 173 | |
| 174 | e = Curl_llist_head(&cshutdn->list); |
| 175 | while(e) { |
| 176 | conn = Curl_node_elem(e); |
| 177 | if(!destination || !strcmp(destination, conn->destination)) |
| 178 | break; |
| 179 | e = Curl_node_next(e); |
| 180 | } |
| 181 | |
| 182 | if(e) { |
| 183 | struct Curl_sigpipe_ctx sigpipe_ctx; |
| 184 | conn = Curl_node_elem(e); |
| 40 | #define CURL_SHARE_KEEP_CONNECT(s) \ |
| C4: CURL_SHARE_KEEP_CONNECT macro returns false when share is NULL | |
| 41 | ((s) && ((s)->specifier & (1 << CURL_LOCK_DATA_CONNECT))) |
| 1742 | CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, |
| 1743 | struct Curl_easy *data, |
| 1744 | struct connectdata *conn) |
| 1745 | { |
| 1746 | CURLMcode mresult; |
| 1747 | |
| 1748 | mresult = Curl_multi_add_handle(multi, data); |
| 1749 | if(!mresult) { |
| 1750 | CURLcode result; |
| 1751 | |
| 1752 | /* pass in NULL for 'conn' here since we do not want to init the |
| 1753 | connection, only this transfer */ |
| 1754 | result = Curl_init_transfer(data, NULL); |
| 1755 | if(result) { |
| 1756 | Curl_multi_remove_handle(multi, data); |
| 1757 | return CURLM_INTERNAL_ERROR; |
| 1758 | } |
| 1759 | |
| 1760 | /* take this handle to the perform state right away */ |
| 1761 | multistate(data, MSTATE_PERFORMING); |
| 1762 | Curl_attach_connection(data, conn); |
| C3: Curl_multi_add_perform calls Curl_attach_connection(data, conn) which sets data->conn = conn | |
| 1763 | CURL_REQ_SET_RECV(data); |
| 1764 | } |
| 1765 | return mresult; |
| 1766 | } |
| 777 | static int push_promise(struct Curl_cfilter *cf, |
| 778 | struct Curl_easy *data, |
| 779 | const nghttp2_push_promise *frame) |
| 780 | { |
| 781 | struct cf_h2_ctx *ctx = cf->ctx; |
| 782 | int rv; /* one of the CURL_PUSH_* defines */ |
| 783 | |
| 784 | CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received", |
| 785 | frame->promised_stream_id); |
| 786 | if(data->multi->push_cb) { |
| 787 | struct h2_stream_ctx *stream; |
| 788 | struct h2_stream_ctx *newstream; |
| 789 | struct curl_pushheaders heads; |
| 790 | CURLMcode mresult; |
| 791 | CURLcode result; |
| 792 | /* clone the parent */ |
| 793 | struct Curl_easy *newhandle = h2_duphandle(cf, data); |
| 794 | if(!newhandle) { |
| 795 | infof(data, "failed to duplicate handle"); |
| 796 | rv = CURL_PUSH_DENY; /* FAIL HARD */ |
| 797 | goto fail; |
| 798 | } |
| 799 | |
| 800 | stream = H2_STREAM_CTX(ctx, data); |
| 801 | if(!stream) { |
| 802 | failf(data, "Internal NULL stream"); |
| 803 | discard_newhandle(cf, newhandle); |
| 804 | rv = CURL_PUSH_DENY; |
| 805 | goto fail; |
| 806 | } |
| 807 | |
| 808 | heads.data = data; |
| 809 | heads.stream = stream; |
| 810 | heads.frame = frame; |
| 811 | |
| 812 | rv = set_transfer_url(newhandle, &heads, data); |
| 813 | if(rv) { |
| 814 | CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d", |
| 815 | frame->promised_stream_id, rv); |
| 816 | discard_newhandle(cf, newhandle); |
| 817 | rv = CURL_PUSH_DENY; |
| 818 | goto fail; |
| 819 | } |
| 820 | |
| 821 | { |
| 822 | struct Curl_mapi_guard guard; |
| 823 | CURL_CBAPI_START(&guard, data, multi_push_cb); |
| 824 | rv = data->multi->push_cb(data, newhandle, |
| 825 | stream->push_headers_used, &heads, |
| 826 | data->multi->push_userp); |
| 827 | CURL_CBAPI_END(&guard); |
| 828 | } |
| 829 | |
| 830 | /* free the headers again */ |
| 831 | free_push_headers(stream); |
| 832 | |
| 833 | if(rv) { |
| 834 | DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); |
| 835 | /* denied, kill off the new handle again */ |
| 836 | CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, denied by application -> %d", |
| 837 | frame->promised_stream_id, rv); |
| 838 | discard_newhandle(cf, newhandle); |
| 839 | goto fail; |
| 840 | } |
| 841 | |
| 842 | /* approved, add to the multi handle for processing. This |
| 843 | * assigns newhandle->mid. For the new `mid` we assign the |
| 844 | * h2_stream instance and remember the stream_id already known. */ |
| 845 | mresult = Curl_multi_add_perform(data->multi, newhandle, cf->conn); |
| C3: push_promise calls Curl_multi_add_perform(data->multi, newhandle, cf->conn) attaching parent's connection | |
| 846 | if(mresult) { |
| 847 | infof(data, "failed to add handle to multi"); |
| 848 | discard_newhandle(cf, newhandle); |
| 849 | rv = CURL_PUSH_DENY; |
| 850 | goto fail; |
| 851 | } |
| 852 | |
| 853 | result = http2_data_setup(cf, newhandle, &newstream); |
| 854 | if(result) { |
| 855 | failf(data, "error setting up stream: %d", (int)result); |
| 856 | discard_newhandle(cf, newhandle); |
| 857 | rv = CURL_PUSH_DENY; |
| 858 | goto fail; |
| 859 | } |
| 860 | |
| 861 | DEBUGASSERT(newstream); |
| 862 | newstream->id = frame->promised_stream_id; |
| 863 | newhandle->req.maxdownload = -1; |
| 864 | newhandle->req.size = -1; |
| 865 | |
| 866 | CURL_TRC_CF(data, cf, "promise easy handle added to multi, mid=%u", |
| 867 | newhandle->mid); |
| 868 | rv = nghttp2_session_set_stream_user_data(ctx->h2, |
| 869 | newstream->id, |
| 870 | newhandle); |
| 871 | if(rv) { |
| 872 | infof(data, "failed to set user_data for stream %d", |
| 873 | newstream->id); |
| 874 | DEBUGASSERT(0); |
| 875 | discard_newhandle(cf, newhandle); |
| 876 | rv = CURL_PUSH_DENY; |
| 877 | goto fail; |
| 878 | } |
| 879 | |
| 880 | /* success, remember max stream id processed */ |
| 881 | if(newstream->id > ctx->local_max_sid) |
| 882 | ctx->local_max_sid = newstream->id; |
| 883 | } |
| 884 | else { |
| 885 | CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it"); |
| 886 | rv = CURL_PUSH_DENY; |
| 887 | } |
| 888 | fail: |
| 889 | return rv; |
| 890 | } |
| 35 | static void share_destroy(struct Curl_share *share) |
| 36 | { |
| 37 | if(!share) |
| 38 | return; |
| 39 | |
| 40 | if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { |
| 41 | Curl_cpool_destroy(&share->cpool); |
| C6: share_destroy calls Curl_cpool_destroy(&share->cpool) | |
| 42 | } |
| 43 | |
| 44 | Curl_dnscache_destroy(&share->dnscache); |
| 45 | |
| 46 | #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) |
| 47 | Curl_cookie_cleanup(share->cookies); |
| 48 | #endif |
| 49 | |
| 50 | #ifndef CURL_DISABLE_HSTS |
| 51 | Curl_hsts_cleanup(&share->hsts); |
| 52 | #endif |
| 53 | |
| 54 | #ifdef USE_SSL |
| 55 | if(share->ssl_scache) { |
| 56 | Curl_ssl_scache_destroy(share->ssl_scache); |
| 57 | share->ssl_scache = NULL; |
| 58 | } |
| 59 | #endif |
| 60 | |
| 61 | Curl_psl_destroy(&share->psl); |
| 62 | Curl_close(&share->admin); |
| 63 | |
| 64 | #ifdef USE_MUTEX |
| 65 | Curl_mutex_destroy(&share->lock); |
| 66 | #endif |
| 67 | curlx_memzero(share, sizeof(*share)); |
| 68 | curlx_free(share); |
| 69 | } |
Show 51 lines (not cited by the judge)
| 11 | * you should have received as part of this distribution. The terms |
| 12 | * are also available at https://curl.se/docs/copyright.html. |
| 13 | * |
| 14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| 15 | * copies of the Software, and permit persons to whom the Software is |
| 16 | * furnished to do so, under the terms of the COPYING file. |
| 17 | * |
| 18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 19 | * KIND, either express or implied. |
| 20 | * |
| 21 | * SPDX-License-Identifier: curl |
| 22 | * |
| 23 | ***************************************************************************/ |
| 24 | #include "curl_setup.h" |
| 25 | |
| 26 | #include "llist.h" |
| 27 | |
| 28 | #ifdef DEBUGBUILD |
| 29 | #define LLISTINIT 0x100cc001 /* random pattern */ |
| 30 | #define NODEINIT 0x12344321 /* random pattern */ |
| 31 | #define NODEREM 0x54321012 /* random pattern */ |
| 32 | |
| 33 | #define VERIFYNODE(x) verifynode(x) |
| 34 | static struct Curl_llist_node *verifynode(struct Curl_llist_node *n) |
| 35 | { |
| 36 | DEBUGASSERT(!n || (n->_init == NODEINIT)); |
| 37 | return n; |
| 38 | } |
| 39 | #else |
| 40 | #define VERIFYNODE(x) x |
| 41 | #endif |
| 42 | /* |
| 43 | * @unittest 1300 |
| 44 | */ |
| 45 | void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) |
| 46 | { |
| 47 | l->_size = 0; |
| 48 | l->_dtor = dtor; |
| 49 | l->_head = NULL; |
| 50 | l->_tail = NULL; |
| 51 | #ifdef DEBUGBUILD |
| 52 | l->_init = LLISTINIT; |
| 53 | #endif |
| 54 | } |
| 55 | |
| 56 | /* |
| 57 | * Curl_llist_insert_next() |
| 58 | * |
| 59 | * Inserts a new list element after the given one 'e'. If the given existing |
| 60 | * entry is NULL and the list already has elements, the new one will be |
| 61 | * inserted first in the list. |
Show 78 lines (not cited by the judge)
| 1157 | match.want_proxy_nego_http = |
| 1158 | needle->http_proxy.creds && |
| 1159 | (data->state.authproxy.want & CURLAUTH_NEGOTIATE) && |
| 1160 | (needle->scheme->protocol & PROTO_FAMILY_HTTP); |
| 1161 | #endif |
| 1162 | #endif |
| 1163 | match.require_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; |
| 1164 | match.may_tls = data->set.use_ssl > CURLUSESSL_NONE; |
| 1165 | |
| 1166 | /* Find a connection in the pool that matches what "data + needle" |
| 1167 | * requires. If a suitable candidate is found, it is attached to "data". */ |
| 1168 | success = Curl_cpool_find(data, needle->destination, |
| 1169 | url_match_conn, url_match_result, &match); |
| 1170 | |
| 1171 | /* wait_pipe is TRUE if we encounter a bundle that is undecided. There |
| 1172 | * is no matching connection then, yet. */ |
| 1173 | *waitpipe = (bool)match.wait_pipe; |
| 1174 | return success; |
| 1175 | } |
| 1176 | |
| 1177 | /* |
| 1178 | * Allocate and initialize a new connectdata object. |
| 1179 | */ |
| 1180 | static struct connectdata *allocate_conn(struct Curl_easy *data) |
| 1181 | { |
| 1182 | struct connectdata *conn = curlx_calloc(1, sizeof(struct connectdata)); |
| 1183 | if(!conn) |
| 1184 | return NULL; |
| 1185 | |
| 1186 | /* and we setup a few fields in case we end up actually using this struct */ |
| 1187 | |
| 1188 | conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ |
| 1189 | conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ |
| 1190 | conn->recv_idx = 0; /* default for receiving transfer data */ |
| 1191 | conn->send_idx = 0; /* default for sending transfer data */ |
| 1192 | conn->connection_id = -1; /* no ID */ |
| 1193 | conn->attached_xfers = 0; |
| 1194 | |
| 1195 | /* Store creation time to help future close decision making */ |
| 1196 | conn->created = *Curl_pgrs_now(data); |
| 1197 | |
| 1198 | /* Store current time to give a baseline to keepalive connection times. */ |
| 1199 | conn->keepalive = conn->created; |
| 1200 | |
| 1201 | #ifndef CURL_DISABLE_FTP |
| 1202 | conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; |
| 1203 | conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; |
| 1204 | #endif |
| 1205 | conn->ip_version = data->set.ipver; |
| 1206 | conn->bits.connect_only = (bool)data->set.connect_only; |
| 1207 | conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ |
| 1208 | |
| 1209 | /* Store the local bind parameters that will be used for this connection */ |
| 1210 | if(data->set.str[STRING_DEVICE]) { |
| 1211 | conn->localdev = curlx_strdup(data->set.str[STRING_DEVICE]); |
| 1212 | if(!conn->localdev) |
| 1213 | goto error; |
| 1214 | } |
| 1215 | #ifndef CURL_DISABLE_BINDLOCAL |
| 1216 | conn->localportrange = data->set.localportrange; |
| 1217 | conn->localport = data->set.localport; |
| 1218 | #endif |
| 1219 | |
| 1220 | /* the close socket stuff needs to be copied to the connection struct as |
| 1221 | it may live on without (this specific) Curl_easy */ |
| 1222 | conn->fclosesocket = data->set.fclosesocket; |
| 1223 | conn->closesocket_client = data->set.closesocket_client; |
| 1224 | conn->lastused = conn->created; |
| 1225 | #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) |
| 1226 | conn->gssapi_delegation = data->set.gssapi_delegation; |
| 1227 | #endif |
| 1228 | return conn; |
| 1229 | error: |
| 1230 | |
| 1231 | curlx_free(conn->localdev); |
| 1232 | curlx_free(conn); |
| 1233 | return NULL; |
| 1234 | } |
Show 131 lines (not cited by the judge)
| 953 | CURL *curl_easy_duphandle(CURL *curl) |
| 954 | { |
| 955 | struct Curl_easy *data = curl; |
| 956 | struct Curl_easy *outcurl = NULL; |
| 957 | |
| 958 | if(!GOOD_EASY_HANDLE(data)) |
| 959 | goto fail; |
| 960 | outcurl = curlx_calloc(1, sizeof(struct Curl_easy)); |
| 961 | if(!outcurl) |
| 962 | goto fail; |
| 963 | |
| 964 | /* |
| 965 | * We setup a few buffers we need. We should probably make them |
| 966 | * get setup on-demand in the code, as that would probably decrease |
| 967 | * the likeliness of us forgetting to init a buffer here in the future. |
| 968 | */ |
| 969 | outcurl->set.buffer_size = data->set.buffer_size; |
| 970 | |
| 971 | Curl_hash_init(&outcurl->meta_hash, 23, |
| 972 | Curl_hash_str, curlx_str_key_compare, dupeasy_meta_freeentry); |
| 973 | curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); |
| 974 | Curl_bufref_init(&outcurl->state.url); |
| 975 | Curl_bufref_init(&outcurl->state.referer); |
| 976 | Curl_netrc_init(&outcurl->state.netrc); |
| 977 | |
| 978 | /* the connection pool is setup on demand */ |
| 979 | outcurl->state.lastconnect_id = -1; |
| 980 | outcurl->state.recent_conn_id = -1; |
| 981 | outcurl->id = -1; |
| 982 | outcurl->mid = UINT32_MAX; |
| 983 | outcurl->master_mid = UINT32_MAX; |
| 984 | |
| 985 | #ifndef CURL_DISABLE_HTTP |
| 986 | Curl_llist_init(&outcurl->state.httphdrs, NULL); |
| 987 | #endif |
| 988 | Curl_initinfo(outcurl); |
| 989 | |
| 990 | /* copy all userdefined values */ |
| 991 | if(dupset(outcurl, data)) |
| 992 | goto fail; |
| 993 | |
| 994 | outcurl->progress.hide = data->progress.hide; |
| 995 | outcurl->progress.callback = data->progress.callback; |
| 996 | |
| 997 | #ifndef CURL_DISABLE_COOKIES |
| 998 | outcurl->state.cookielist = NULL; |
| 999 | if(data->cookies && data->state.cookie_engine) { |
| 1000 | /* If cookies are enabled in the parent handle, we enable them |
| 1001 | in the clone as well! */ |
| 1002 | outcurl->cookies = Curl_cookie_init(); |
| 1003 | if(!outcurl->cookies) |
| 1004 | goto fail; |
| 1005 | outcurl->state.cookie_engine = TRUE; |
| 1006 | } |
| 1007 | |
| 1008 | if(data->state.cookielist) { |
| 1009 | outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); |
| 1010 | if(!outcurl->state.cookielist) |
| 1011 | goto fail; |
| 1012 | } |
| 1013 | #endif |
| 1014 | |
| 1015 | if(Curl_bufref_ptr(&data->state.url)) { |
| 1016 | Curl_bufref_set(&outcurl->state.url, |
| 1017 | Curl_bufref_dup(&data->state.url), 0, |
| 1018 | curl_free); |
| 1019 | if(!Curl_bufref_ptr(&outcurl->state.url)) |
| 1020 | goto fail; |
| 1021 | } |
| 1022 | if(Curl_bufref_ptr(&data->state.referer)) { |
| 1023 | Curl_bufref_set(&outcurl->state.referer, |
| 1024 | Curl_bufref_dup(&data->state.referer), 0, |
| 1025 | curl_free); |
| 1026 | if(!Curl_bufref_ptr(&outcurl->state.referer)) |
| 1027 | goto fail; |
| 1028 | } |
| 1029 | |
| 1030 | /* Reinitialize an SSL engine for the new handle |
| 1031 | * note: the engine name has already been copied by dupset */ |
| 1032 | if(outcurl->set.str[STRING_SSL_ENGINE]) { |
| 1033 | if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) |
| 1034 | goto fail; |
| 1035 | } |
| 1036 | |
| 1037 | #ifndef CURL_DISABLE_ALTSVC |
| 1038 | if(data->asi) { |
| 1039 | outcurl->asi = Curl_altsvc_init(); |
| 1040 | if(!outcurl->asi) |
| 1041 | goto fail; |
| 1042 | if(outcurl->set.str[STRING_ALTSVC]) |
| 1043 | (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]); |
| 1044 | } |
| 1045 | #endif |
| 1046 | #ifndef CURL_DISABLE_HSTS |
| 1047 | if(data->hsts) { |
| 1048 | outcurl->hsts = Curl_hsts_init(); |
| 1049 | if(!outcurl->hsts) |
| 1050 | goto fail; |
| 1051 | if(outcurl->set.str[STRING_HSTS]) |
| 1052 | (void)Curl_hsts_loadfile(outcurl, |
| 1053 | outcurl->hsts, outcurl->set.str[STRING_HSTS]); |
| 1054 | (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); |
| 1055 | |
| 1056 | /* Copy entries learned at runtime. (E.g. Strict-Transport-Security |
| 1057 | headers.) */ |
| 1058 | if(Curl_hsts_copy(outcurl->hsts, data->hsts)) |
| 1059 | goto fail; |
| 1060 | } |
| 1061 | #endif |
| 1062 | |
| 1063 | outcurl->magic = CURLEASY_MAGIC_NUMBER; |
| 1064 | |
| 1065 | /* we reach this point and thus we are OK */ |
| 1066 | |
| 1067 | return outcurl; |
| 1068 | |
| 1069 | fail: |
| 1070 | |
| 1071 | if(outcurl) { |
| 1072 | #ifndef CURL_DISABLE_COOKIES |
| 1073 | curlx_free(outcurl->cookies); |
| 1074 | #endif |
| 1075 | curlx_dyn_free(&outcurl->state.headerb); |
| 1076 | Curl_altsvc_cleanup(&outcurl->asi); |
| 1077 | Curl_hsts_cleanup(&outcurl->hsts); |
| 1078 | Curl_freeset(outcurl); |
| 1079 | curlx_free(outcurl); |
| 1080 | } |
| 1081 | |
| 1082 | return NULL; |
| 1083 | } |
Show 1 lines (not cited by the judge)
| 505 | #define CURL_PUSH_OK 0 |
Show 1 lines (not cited by the judge)
| 421 | #define CURL_SOCKOPT_ALREADY_CONNECTED 2 |
Show 17 lines (not cited by the judge)
| 945 | void Curl_attach_connection(struct Curl_easy *data, |
| 946 | struct connectdata *conn) |
| 947 | { |
| 948 | DEBUGASSERT(data); |
| 949 | DEBUGASSERT(!data->conn); |
| 950 | DEBUGASSERT(conn); |
| 951 | DEBUGASSERT(conn->attached_xfers < UINT32_MAX); |
| 952 | data->conn = conn; |
| 953 | conn->attached_xfers++; |
| 954 | /* all attached transfers must be from the same multi */ |
| 955 | if(!conn->attached_multi) |
| 956 | conn->attached_multi = data->multi; |
| 957 | DEBUGASSERT(conn->attached_multi == data->multi); |
| 958 | |
| 959 | if(conn->scheme && conn->scheme->run->attach) |
| 960 | conn->scheme->run->attach(data, conn); |
| 961 | } |
Show 51 lines (not cited by the judge)
| 652 | void Curl_conn_terminate(struct Curl_easy *data, |
| 653 | struct connectdata *conn, |
| 654 | bool aborted) |
| 655 | { |
| 656 | struct cpool *cpool = cpool_get_instance(data); |
| 657 | bool do_lock; |
| 658 | |
| 659 | DEBUGASSERT(cpool); |
| 660 | DEBUGASSERT(data && !data->conn); |
| 661 | if(!cpool) |
| 662 | return; |
| 663 | |
| 664 | /* If this connection is not marked to force-close, leave it open if there |
| 665 | * are other users of it */ |
| 666 | if(CONN_INUSE(conn) && !aborted) { |
| 667 | DEBUGASSERT(0); /* does this ever happen? */ |
| 668 | DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers)); |
| 669 | return; |
| 670 | } |
| 671 | |
| 672 | /* This method may be called while we are under lock, e.g. from a |
| 673 | * user callback in find. */ |
| 674 | do_lock = !CPOOL_IS_LOCKED(cpool); |
| 675 | if(do_lock) |
| 676 | CPOOL_LOCK(cpool, data); |
| 677 | |
| 678 | if(conn->bits.in_cpool) { |
| 679 | cpool_remove_conn(cpool, conn); |
| 680 | DEBUGASSERT(!conn->bits.in_cpool); |
| 681 | } |
| 682 | |
| 683 | /* treat the connection as aborted in CONNECT_ONLY situations, |
| 684 | * so no graceful shutdown is attempted. */ |
| 685 | if(conn->bits.connect_only) |
| 686 | aborted = TRUE; |
| 687 | |
| 688 | if(data->multi) { |
| 689 | /* Add it to the multi's cpool for shutdown handling */ |
| 690 | infof(data, "%s connection #%" FMT_OFF_T, |
| 691 | aborted ? "closing" : "shutting down", conn->connection_id); |
| 692 | cpool_discard_conn(&data->multi->cpool, data, conn, aborted); |
| 693 | } |
| 694 | else { |
| 695 | /* No multi available, terminate */ |
| 696 | infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id); |
| 697 | Curl_cshutdn_terminate(cpool->idata, conn, !aborted); |
| 698 | } |
| 699 | |
| 700 | if(do_lock) |
| 701 | CPOOL_UNLOCK(cpool, data); |
| 702 | } |
Show 13 lines (not cited by the judge)
| 826 | void Curl_cpool_do_locked(struct Curl_easy *data, |
| 827 | struct connectdata *conn, |
| 828 | Curl_cpool_conn_do_cb *cb, void *cbdata) |
| 829 | { |
| 830 | struct cpool *cpool = cpool_get_instance(data); |
| 831 | if(cpool) { |
| 832 | CPOOL_LOCK(cpool, data); |
| 833 | cb(conn, data, cbdata); |
| 834 | CPOOL_UNLOCK(cpool, data); |
| 835 | } |
| 836 | else |
| 837 | cb(conn, data, cbdata); |
| 838 | } |
Show 28 lines (not cited by the judge)
| 1749 | CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, |
| 1750 | struct Curl_easy *data, |
| 1751 | struct connectdata *conn) |
| 1752 | { |
| 1753 | CURLMcode mresult; |
| 1754 | |
| 1755 | if(multi->in_callback) |
| 1756 | return CURLM_RECURSIVE_API_CALL; |
| 1757 | |
| 1758 | mresult = curl_multi_add_handle(multi, data); |
| 1759 | if(!mresult) { |
| 1760 | CURLcode result; |
| 1761 | |
| 1762 | /* pass in NULL for 'conn' here since we do not want to init the |
| 1763 | connection, only this transfer */ |
| 1764 | result = Curl_init_do(data, NULL); |
| 1765 | if(result) { |
| 1766 | curl_multi_remove_handle(multi, data); |
| 1767 | return CURLM_INTERNAL_ERROR; |
| 1768 | } |
| 1769 | |
| 1770 | /* take this handle to the perform state right away */ |
| 1771 | multistate(data, MSTATE_PERFORMING); |
| 1772 | Curl_attach_connection(data, conn); |
| 1773 | CURL_REQ_SET_RECV(data); |
| 1774 | } |
| 1775 | return mresult; |
| 1776 | } |
Show 1 lines (not cited by the judge)
| 1077 | #define DEBUGASSERT(x) CURL_DEBUGASSERT(x) |
Show 15 lines (not cited by the judge)
| 897 | CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) |
| 898 | { |
| 899 | struct Curl_mapi_guard guard; |
| 900 | CURLMcode mresult; |
| 901 | |
| 902 | if(CURL_MAPI_ENTER(&guard, m, multi_remove_handle, &mresult)) { |
| 903 | struct Curl_easy *data = curl; |
| 904 | if(!GOOD_EASY_HANDLE(data)) |
| 905 | mresult = CURLM_BAD_EASY_HANDLE; |
| 906 | else |
| 907 | mresult = Curl_multi_remove_handle(m, data); |
| 908 | } |
| 909 | CURL_MAPI_LEAVE(&guard); |
| 910 | return mresult; |
| 911 | } |
Show 155 lines (not cited by the judge)
| 784 | CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) |
| 785 | { |
| 786 | struct Curl_multi *multi = m; |
| 787 | struct Curl_easy *data = curl; |
| 788 | bool premature; |
| 789 | struct Curl_llist_node *e; |
| 790 | CURLMcode mresult; |
| 791 | uint32_t mid; |
| 792 | |
| 793 | /* First, make some basic checks that the CURLM handle is a good handle */ |
| 794 | if(!GOOD_MULTI_HANDLE(multi)) |
| 795 | return CURLM_BAD_HANDLE; |
| 796 | |
| 797 | /* Verify that we got a somewhat good easy handle too */ |
| 798 | if(!GOOD_EASY_HANDLE(data)) |
| 799 | return CURLM_BAD_EASY_HANDLE; |
| 800 | |
| 801 | /* Prevent users from trying to remove same easy handle more than once */ |
| 802 | if(!data->multi) |
| 803 | return CURLM_OK; /* it is already removed so let's say it is fine! */ |
| 804 | |
| 805 | /* Prevent users from trying to remove an easy handle from the wrong multi */ |
| 806 | if(data->multi != multi) |
| 807 | return CURLM_BAD_EASY_HANDLE; |
| 808 | |
| 809 | if(data->mid == UINT32_MAX) { |
| 810 | DEBUGASSERT(0); |
| 811 | return CURLM_INTERNAL_ERROR; |
| 812 | } |
| 813 | if(Curl_uint32_tbl_get(&multi->xfers, data->mid) != data) { |
| 814 | DEBUGASSERT(0); |
| 815 | return CURLM_INTERNAL_ERROR; |
| 816 | } |
| 817 | |
| 818 | if(multi->in_callback) |
| 819 | return CURLM_RECURSIVE_API_CALL; |
| 820 | |
| 821 | premature = (data->mstate < MSTATE_COMPLETED); |
| 822 | |
| 823 | /* If the 'state' is not INIT or COMPLETED, we might need to do something |
| 824 | nice to put the easy_handle in a good known state when this returns. */ |
| 825 | if(data->conn && |
| 826 | data->mstate > MSTATE_DO && |
| 827 | data->mstate < MSTATE_COMPLETED) { |
| 828 | /* Set connection owner so that the DONE function closes it. We can |
| 829 | safely do this here since connection is killed. */ |
| 830 | streamclose(data->conn, "Removed with partial response"); |
| 831 | } |
| 832 | |
| 833 | if(data->conn) { |
| 834 | /* multi_done() clears the association between the easy handle and the |
| 835 | connection. |
| 836 | |
| 837 | Note that this ignores the return code because there is |
| 838 | nothing really useful to do with it anyway! */ |
| 839 | (void)multi_done(data, data->result, premature); |
| 840 | } |
| 841 | |
| 842 | /* The timer must be shut down before data->multi is set to NULL, else the |
| 843 | timenode will remain in the splay tree after curl_easy_cleanup is |
| 844 | called. Do it after multi_done() in case that sets another time! */ |
| 845 | Curl_expire_clear(data); |
| 846 | |
| 847 | /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */ |
| 848 | if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) |
| 849 | --multi->xfers_alive; |
| 850 | if(data->state.really_alive) { |
| 851 | data->state.really_alive = FALSE; |
| 852 | --multi->xfers_really_alive; |
| 853 | if(!multi->xfers_really_alive) |
| 854 | (void)multi_assess_wakeup(multi); |
| 855 | } |
| 856 | |
| 857 | Curl_wildcard_dtor(&data->wildcard); |
| 858 | |
| 859 | data->mstate = MSTATE_COMPLETED; |
| 860 | |
| 861 | /* Remove the association between the connection and the handle */ |
| 862 | Curl_detach_connection(data); |
| 863 | |
| 864 | /* Tell event handling that this transfer is definitely going away */ |
| 865 | Curl_multi_ev_xfer_done(multi, data); |
| 866 | |
| 867 | if(data->set.connect_only && !data->multi_easy) { |
| 868 | /* This removes a handle that was part the multi interface that used |
| 869 | CONNECT_ONLY, that connection is now left alive but since this handle |
| 870 | has bits.close set nothing can use that transfer anymore and it is |
| 871 | forbidden from reuse. This easy handle cannot find the connection |
| 872 | anymore once removed from the multi handle |
| 873 | |
| 874 | Better close the connection here, at once. |
| 875 | */ |
| 876 | struct connectdata *c; |
| 877 | curl_socket_t s; |
| 878 | s = Curl_getconnectinfo(data, &c); |
| 879 | if((s != CURL_SOCKET_BAD) && c) { |
| 880 | Curl_conn_terminate(data, c, TRUE); |
| 881 | } |
| 882 | } |
| 883 | |
| 884 | if(data->state.lastconnect_id != -1) { |
| 885 | /* Mark any connect-only connection for closure */ |
| 886 | Curl_cpool_do_by_id(data, data->state.lastconnect_id, |
| 887 | close_connect_only, NULL); |
| 888 | } |
| 889 | |
| 890 | #ifdef USE_LIBPSL |
| 891 | /* Remove the PSL association. */ |
| 892 | if(data->psl == &multi->psl) |
| 893 | data->psl = NULL; |
| 894 | #endif |
| 895 | |
| 896 | /* make sure there is no pending message in the queue sent from this easy |
| 897 | handle */ |
| 898 | for(e = Curl_llist_head(&multi->msglist); e; e = Curl_node_next(e)) { |
| 899 | struct Curl_message *msg = Curl_node_elem(e); |
| 900 | |
| 901 | if(msg->extmsg.easy_handle == data) { |
| 902 | Curl_node_remove(e); |
| 903 | /* there can only be one from this specific handle */ |
| 904 | break; |
| 905 | } |
| 906 | } |
| 907 | |
| 908 | /* clear the association to this multi handle */ |
| 909 | mid = data->mid; |
| 910 | DEBUGASSERT(Curl_uint32_tbl_contains(&multi->xfers, mid)); |
| 911 | Curl_uint32_tbl_remove(&multi->xfers, mid); |
| 912 | Curl_uint32_bset_remove(&multi->process, mid); |
| 913 | Curl_uint32_bset_remove(&multi->dirty, mid); |
| 914 | Curl_uint32_bset_remove(&multi->pending, mid); |
| 915 | Curl_uint32_bset_remove(&multi->msgsent, mid); |
| 916 | data->multi = NULL; |
| 917 | data->mid = UINT32_MAX; |
| 918 | data->master_mid = UINT32_MAX; |
| 919 | |
| 920 | /* NOTE NOTE NOTE |
| 921 | We do not touch the easy handle here! */ |
| 922 | process_pending_handles(multi); |
| 923 | |
| 924 | mresult = Curl_update_timer(multi); |
| 925 | if(mresult) |
| 926 | return mresult; |
| 927 | |
| 928 | mresult = multi_assess_wakeup(multi); |
| 929 | if(mresult) { |
| 930 | failf(data, "error enabling wakeup listening: %d", mresult); |
| 931 | return mresult; |
| 932 | } |
| 933 | |
| 934 | CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u", |
| 935 | mid, Curl_multi_xfers_running(multi), |
| 936 | Curl_uint32_tbl_count(&multi->xfers)); |
| 937 | return CURLM_OK; |
| 938 | } |
Show 14 lines (not cited by the judge)
| 359 | CURLSHcode curl_share_cleanup(CURLSH *sh) |
| 360 | { |
| 361 | struct Curl_share *share = sh; |
| 362 | bool locked; |
| 363 | if(!GOOD_SHARE_HANDLE(share)) |
| 364 | return CURLSHE_INVALID; |
| 365 | |
| 366 | if(share_in_use(share)) |
| 367 | return CURLSHE_IN_USE; |
| 368 | |
| 369 | locked = share_lock_acquire(share, NULL); |
| 370 | share_unlink(&share, NULL, locked); |
| 371 | return CURLSHE_OK; |
| 372 | } |
Show 79 lines (not cited by the judge)
| 678 | static CURLcode multi_done(struct Curl_easy *data, |
| 679 | CURLcode status, /* an error if this is called |
| 680 | after an error was detected */ |
| 681 | bool premature) |
| 682 | { |
| 683 | CURLcode result; |
| 684 | struct connectdata *conn = data->conn; |
| 685 | |
| 686 | CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d", |
| 687 | (int)status, (int)premature, data->state.done); |
| 688 | |
| 689 | if(data->state.done) |
| 690 | /* Stop if multi_done() has already been called */ |
| 691 | return CURLE_OK; |
| 692 | |
| 693 | /* Shut down any ongoing async resolver operation. */ |
| 694 | Curl_resolv_shutdown_all(data); |
| 695 | |
| 696 | /* Cleanup possible redirect junk */ |
| 697 | curlx_safefree(data->req.newurl); |
| 698 | curlx_safefree(data->req.location); |
| 699 | |
| 700 | switch(status) { |
| 701 | case CURLE_ABORTED_BY_CALLBACK: |
| 702 | case CURLE_READ_ERROR: |
| 703 | case CURLE_WRITE_ERROR: |
| 704 | /* When we are aborted due to a callback return code it has to be counted |
| 705 | as premature as there is trouble ahead if we do not. We have many |
| 706 | callbacks and protocols work differently, we could potentially do this |
| 707 | more fine-grained in the future. */ |
| 708 | premature = TRUE; |
| 709 | FALLTHROUGH(); |
| 710 | default: |
| 711 | break; |
| 712 | } |
| 713 | |
| 714 | /* this calls the protocol-specific function pointer previously set */ |
| 715 | if(conn && conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT)) |
| 716 | result = conn->scheme->run->done(data, status, premature); |
| 717 | else |
| 718 | result = status; |
| 719 | |
| 720 | if(data->mstate > MSTATE_CONNECTING && |
| 721 | (result != CURLE_ABORTED_BY_CALLBACK)) { |
| 722 | /* avoid this if |
| 723 | * - the transfer has not connected |
| 724 | * - we already aborted by callback to avoid this calling another callback |
| 725 | */ |
| 726 | int rc = Curl_pgrsDone(data); |
| 727 | if(!result && rc) |
| 728 | result = CURLE_ABORTED_BY_CALLBACK; |
| 729 | } |
| 730 | |
| 731 | /* Make sure that transfer client writes are really done now. */ |
| 732 | result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature)); |
| 733 | |
| 734 | /* Inform connection filters that this transfer is done */ |
| 735 | if(conn) |
| 736 | Curl_conn_ev_data_done(data, premature); |
| 737 | |
| 738 | multi_schedule_pending(data->multi); /* connection / multiplex */ |
| 739 | |
| 740 | if(!result) |
| 741 | result = Curl_req_done(&data->req, data, premature); |
| 742 | |
| 743 | if(conn) { |
| 744 | /* Under the potential connection pool's share lock, decide what to |
| 745 | * do with the transfer's connection. */ |
| 746 | struct multi_done_ctx mdctx; |
| 747 | |
| 748 | memset(&mdctx, 0, sizeof(mdctx)); |
| 749 | mdctx.premature = premature; |
| 750 | Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); |
| 751 | } |
| 752 | |
| 753 | /* flush the netrc cache */ |
| 754 | Curl_netrc_cleanup(&data->state.netrc); |
| 755 | return result; |
| 756 | } |
Show 79 lines (not cited by the judge)
| 694 | static CURLcode multi_done(struct Curl_easy *data, |
| 695 | CURLcode status, /* an error if this is called |
| 696 | after an error was detected */ |
| 697 | bool premature) |
| 698 | { |
| 699 | CURLcode result; |
| 700 | struct connectdata *conn = data->conn; |
| 701 | |
| 702 | CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d", |
| 703 | (int)status, (int)premature, data->state.done); |
| 704 | |
| 705 | if(data->state.done) |
| 706 | /* Stop if multi_done() has already been called */ |
| 707 | return CURLE_OK; |
| 708 | |
| 709 | /* Shut down any ongoing async resolver operation. */ |
| 710 | Curl_resolv_shutdown_all(data); |
| 711 | |
| 712 | /* Cleanup possible redirect junk */ |
| 713 | curlx_safefree(data->req.newurl); |
| 714 | curlx_safefree(data->req.location); |
| 715 | |
| 716 | switch(status) { |
| 717 | case CURLE_ABORTED_BY_CALLBACK: |
| 718 | case CURLE_READ_ERROR: |
| 719 | case CURLE_WRITE_ERROR: |
| 720 | /* When we are aborted due to a callback return code it has to be counted |
| 721 | as premature as there is trouble ahead if we do not. We have many |
| 722 | callbacks and protocols work differently, we could potentially do this |
| 723 | more fine-grained in the future. */ |
| 724 | premature = TRUE; |
| 725 | FALLTHROUGH(); |
| 726 | default: |
| 727 | break; |
| 728 | } |
| 729 | |
| 730 | /* this calls the protocol-specific function pointer previously set */ |
| 731 | if(conn && conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT)) |
| 732 | result = conn->scheme->run->done(data, status, premature); |
| 733 | else |
| 734 | result = status; |
| 735 | |
| 736 | if(data->mstate > MSTATE_CONNECTING && |
| 737 | (result != CURLE_ABORTED_BY_CALLBACK)) { |
| 738 | /* avoid this if |
| 739 | * - the transfer has not connected |
| 740 | * - we already aborted by callback to avoid this calling another callback |
| 741 | */ |
| 742 | int rc = Curl_pgrsDone(data); |
| 743 | if(!result && rc) |
| 744 | result = CURLE_ABORTED_BY_CALLBACK; |
| 745 | } |
| 746 | |
| 747 | /* Make sure that transfer client writes are really done now. */ |
| 748 | result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature)); |
| 749 | |
| 750 | /* Inform connection filters that this transfer is done */ |
| 751 | if(conn) |
| 752 | Curl_conn_ev_data_done(data, premature); |
| 753 | |
| 754 | process_pending_handles(data->multi); /* connection / multiplex */ |
| 755 | |
| 756 | if(!result) |
| 757 | result = Curl_req_done(&data->req, data, premature); |
| 758 | |
| 759 | if(conn) { |
| 760 | /* Under the potential connection pool's share lock, decide what to |
| 761 | * do with the transfer's connection. */ |
| 762 | struct multi_done_ctx mdctx; |
| 763 | |
| 764 | memset(&mdctx, 0, sizeof(mdctx)); |
| 765 | mdctx.premature = premature; |
| 766 | Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); |
| 767 | } |
| 768 | |
| 769 | /* flush the netrc cache */ |
| 770 | Curl_netrc_cleanup(&data->state.netrc); |
| 771 | return result; |
| 772 | } |
Show 111 lines (not cited by the judge)
| 777 | static int push_promise(struct Curl_cfilter *cf, |
| 778 | struct Curl_easy *data, |
| 779 | const nghttp2_push_promise *frame) |
| 780 | { |
| 781 | struct cf_h2_ctx *ctx = cf->ctx; |
| 782 | int rv; /* one of the CURL_PUSH_* defines */ |
| 783 | |
| 784 | CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received", |
| 785 | frame->promised_stream_id); |
| 786 | if(data->multi->push_cb) { |
| 787 | struct h2_stream_ctx *stream; |
| 788 | struct h2_stream_ctx *newstream; |
| 789 | struct curl_pushheaders heads; |
| 790 | CURLMcode mresult; |
| 791 | CURLcode result; |
| 792 | /* clone the parent */ |
| 793 | struct Curl_easy *newhandle = h2_duphandle(cf, data); |
| 794 | if(!newhandle) { |
| 795 | infof(data, "failed to duplicate handle"); |
| 796 | rv = CURL_PUSH_DENY; /* FAIL HARD */ |
| 797 | goto fail; |
| 798 | } |
| 799 | |
| 800 | stream = H2_STREAM_CTX(ctx, data); |
| 801 | if(!stream) { |
| 802 | failf(data, "Internal NULL stream"); |
| 803 | discard_newhandle(cf, newhandle); |
| 804 | rv = CURL_PUSH_DENY; |
| 805 | goto fail; |
| 806 | } |
| 807 | |
| 808 | heads.data = data; |
| 809 | heads.stream = stream; |
| 810 | heads.frame = frame; |
| 811 | |
| 812 | rv = set_transfer_url(newhandle, &heads, data); |
| 813 | if(rv) { |
| 814 | CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d", |
| 815 | frame->promised_stream_id, rv); |
| 816 | discard_newhandle(cf, newhandle); |
| 817 | rv = CURL_PUSH_DENY; |
| 818 | goto fail; |
| 819 | } |
| 820 | |
| 821 | Curl_set_in_callback(data, TRUE); |
| 822 | rv = data->multi->push_cb(data, newhandle, |
| 823 | stream->push_headers_used, &heads, |
| 824 | data->multi->push_userp); |
| 825 | Curl_set_in_callback(data, FALSE); |
| 826 | |
| 827 | /* free the headers again */ |
| 828 | free_push_headers(stream); |
| 829 | |
| 830 | if(rv) { |
| 831 | DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); |
| 832 | /* denied, kill off the new handle again */ |
| 833 | CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, denied by application -> %d", |
| 834 | frame->promised_stream_id, rv); |
| 835 | discard_newhandle(cf, newhandle); |
| 836 | goto fail; |
| 837 | } |
| 838 | |
| 839 | /* approved, add to the multi handle for processing. This |
| 840 | * assigns newhandle->mid. For the new `mid` we assign the |
| 841 | * h2_stream instance and remember the stream_id already known. */ |
| 842 | mresult = Curl_multi_add_perform(data->multi, newhandle, cf->conn); |
| 843 | if(mresult) { |
| 844 | infof(data, "failed to add handle to multi"); |
| 845 | discard_newhandle(cf, newhandle); |
| 846 | rv = CURL_PUSH_DENY; |
| 847 | goto fail; |
| 848 | } |
| 849 | |
| 850 | result = http2_data_setup(cf, newhandle, &newstream); |
| 851 | if(result) { |
| 852 | failf(data, "error setting up stream: %d", (int)result); |
| 853 | discard_newhandle(cf, newhandle); |
| 854 | rv = CURL_PUSH_DENY; |
| 855 | goto fail; |
| 856 | } |
| 857 | |
| 858 | DEBUGASSERT(newstream); |
| 859 | newstream->id = frame->promised_stream_id; |
| 860 | newhandle->req.maxdownload = -1; |
| 861 | newhandle->req.size = -1; |
| 862 | |
| 863 | CURL_TRC_CF(data, cf, "promise easy handle added to multi, mid=%u", |
| 864 | newhandle->mid); |
| 865 | rv = nghttp2_session_set_stream_user_data(ctx->h2, |
| 866 | newstream->id, |
| 867 | newhandle); |
| 868 | if(rv) { |
| 869 | infof(data, "failed to set user_data for stream %d", |
| 870 | newstream->id); |
| 871 | DEBUGASSERT(0); |
| 872 | discard_newhandle(cf, newhandle); |
| 873 | rv = CURL_PUSH_DENY; |
| 874 | goto fail; |
| 875 | } |
| 876 | |
| 877 | /* success, remember max stream id processed */ |
| 878 | if(newstream->id > ctx->local_max_sid) |
| 879 | ctx->local_max_sid = newstream->id; |
| 880 | } |
| 881 | else { |
| 882 | CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it"); |
| 883 | rv = CURL_PUSH_DENY; |
| 884 | } |
| 885 | fail: |
| 886 | return rv; |
| 887 | } |
Show 32 lines (not cited by the judge)
| 35 | static void share_destroy(struct Curl_share *share) |
| 36 | { |
| 37 | if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { |
| 38 | Curl_cpool_destroy(&share->cpool); |
| 39 | } |
| 40 | |
| 41 | Curl_dnscache_destroy(&share->dnscache); |
| 42 | |
| 43 | #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) |
| 44 | Curl_cookie_cleanup(share->cookies); |
| 45 | #endif |
| 46 | |
| 47 | #ifndef CURL_DISABLE_HSTS |
| 48 | Curl_hsts_cleanup(&share->hsts); |
| 49 | #endif |
| 50 | |
| 51 | #ifdef USE_SSL |
| 52 | if(share->ssl_scache) { |
| 53 | Curl_ssl_scache_destroy(share->ssl_scache); |
| 54 | share->ssl_scache = NULL; |
| 55 | } |
| 56 | #endif |
| 57 | |
| 58 | Curl_psl_destroy(&share->psl); |
| 59 | Curl_close(&share->admin); |
| 60 | |
| 61 | #ifdef USE_MUTEX |
| 62 | Curl_mutex_destroy(&share->lock); |
| 63 | #endif |
| 64 | share->magic = 0; |
| 65 | curlx_free(share); |
| 66 | } |