aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAnders Bakken <agbakken@gmail.com>2016-11-14 15:32:00 -0800
committerDaniel Stenberg <daniel@haxx.se>2016-11-28 15:06:17 +0100
commit421f740164cf53ef9dfb6bc96d0b6a8321b32fd5 (patch)
tree48163d29ba399896d614e06301cf1e559bf9df6b /lib
parenta387d881ecf1cfe8def1460fdf2faa3fdef66302 (diff)
http2: Fix crashes when parent stream gets aborted
Closes #1125
Diffstat (limited to 'lib')
-rw-r--r--lib/http2.c76
-rw-r--r--lib/http2.h8
-rw-r--r--lib/url.c9
-rw-r--r--lib/urldata.h7
4 files changed, 97 insertions, 3 deletions
diff --git a/lib/http2.c b/lib/http2.c
index 2ef173140..16684da9d 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -2108,6 +2108,82 @@ CURLcode Curl_http2_switched(struct connectdata *conn,
return CURLE_OK;
}
+void Curl_http2_add_child(struct Curl_easy *parent, struct Curl_easy *child,
+ bool exclusive)
+{
+ struct Curl_http2_dep **tail;
+ struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
+ dep->data = child;
+
+ if(parent->set.stream_dependents && exclusive) {
+ struct Curl_http2_dep *node = parent->set.stream_dependents;
+ while(node) {
+ node->data->set.stream_depends_on = child;
+ node = node->next;
+ }
+
+ tail = &child->set.stream_dependents;
+ while(*tail)
+ tail = &(*tail)->next;
+
+ DEBUGASSERT(!*tail);
+ *tail = parent->set.stream_dependents;
+ parent->set.stream_dependents = 0;
+ }
+
+ tail = &parent->set.stream_dependents;
+ while(*tail) {
+ (*tail)->data->set.stream_depends_e = FALSE;
+ tail = &(*tail)->next;
+ }
+
+ DEBUGASSERT(!*tail);
+ *tail = dep;
+
+ child->set.stream_depends_on = parent;
+ child->set.stream_depends_e = exclusive;
+}
+
+void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
+{
+ struct Curl_http2_dep *last = 0;
+ struct Curl_http2_dep *data = parent->set.stream_dependents;
+ DEBUGASSERT(child->set.stream_depends_on == parent);
+
+ while(data && data->data != child) {
+ last = data;
+ data = data->next;
+ }
+
+ DEBUGASSERT(data);
+
+ if(data) {
+ if(last) {
+ last->next = data->next;
+ }
+ else {
+ parent->set.stream_dependents = data->next;
+ }
+ free(data);
+ }
+
+ child->set.stream_depends_on = 0;
+ child->set.stream_depends_e = FALSE;
+}
+
+void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
+{
+ while(data->set.stream_dependents) {
+ struct Curl_easy *tmp = data->set.stream_dependents->data;
+ Curl_http2_remove_child(data, tmp);
+ if(data->set.stream_depends_on)
+ Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
+ }
+
+ if(data->set.stream_depends_on)
+ Curl_http2_remove_child(data->set.stream_depends_on, data);
+}
+
#else /* !USE_NGHTTP2 */
/* Satisfy external references even if http2 is not compiled in. */
diff --git a/lib/http2.h b/lib/http2.h
index 891753590..f405b3aeb 100644
--- a/lib/http2.h
+++ b/lib/http2.h
@@ -53,6 +53,11 @@ void Curl_http2_setup_conn(struct connectdata *conn);
void Curl_http2_setup_req(struct Curl_easy *data);
void Curl_http2_done(struct connectdata *conn, bool premature);
CURLcode Curl_http2_done_sending(struct connectdata *conn);
+void Curl_http2_add_child(struct Curl_easy *parent, struct Curl_easy *child,
+ bool exclusive);
+void Curl_http2_remove_child(struct Curl_easy *parent,
+ struct Curl_easy *child);
+void Curl_http2_cleanup_dependencies(struct Curl_easy *data);
#else /* USE_NGHTTP2 */
#define Curl_http2_init(x) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL
@@ -65,6 +70,9 @@ CURLcode Curl_http2_done_sending(struct connectdata *conn);
#define Curl_http2_init_userset(x)
#define Curl_http2_done(x,y)
#define Curl_http2_done_sending(x)
+#define Curl_http2_add_child(x, y, z)
+#define Curl_http2_remove_child(x, y)
+#define Curl_http2_cleanup_dependencies(x)
#endif
#endif /* HEADER_CURL_HTTP2_H */
diff --git a/lib/url.c b/lib/url.c
index c1c3a931b..9ee1e6cec 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -464,6 +464,7 @@ CURLcode Curl_close(struct Curl_easy *data)
/* this destroys the channel and we cannot use it anymore after this */
Curl_resolver_cleanup(data->state.resolver);
+ Curl_http2_cleanup_dependencies(data);
Curl_convert_close(data);
/* No longer a dirty share, if it exists */
@@ -2847,9 +2848,11 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
return CURLE_NOT_BUILT_IN;
#else
struct Curl_easy *dep = va_arg(param, struct Curl_easy *);
- if(dep && GOOD_EASY_HANDLE(dep)) {
- data->set.stream_depends_on = dep;
- data->set.stream_depends_e = (option == CURLOPT_STREAM_DEPENDS_E);
+ if(!dep || GOOD_EASY_HANDLE(dep)) {
+ if(data->set.stream_depends_on) {
+ Curl_http2_remove_child(data->set.stream_depends_on, data);
+ }
+ Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E));
}
break;
#endif
diff --git a/lib/urldata.h b/lib/urldata.h
index ccad53e04..595e797b7 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1280,6 +1280,11 @@ struct auth {
be RFC compliant */
};
+struct Curl_http2_dep {
+ struct Curl_http2_dep *next;
+ struct Curl_easy *data;
+};
+
struct UrlState {
/* Points to the connection cache */
@@ -1747,6 +1752,8 @@ struct UserDefined {
struct Curl_easy *stream_depends_on;
bool stream_depends_e; /* set or don't set the Exclusive bit */
int stream_weight;
+
+ struct Curl_http2_dep *stream_dependents;
};
struct Names {