diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
index 031cebb7c3a76182631c19674914181c0062273a..68d923e7e444671c577d863739b641effa8b1301 100644
--- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
@@ -176,6 +176,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
   grpc_security_status status = GRPC_SECURITY_ERROR;
   grpc_server_security_connector *sc = NULL;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_error *err = GRPC_ERROR_NONE;
 
   GRPC_API_TRACE(
       "grpc_server_add_secure_http2_port("
@@ -214,11 +215,12 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
   gpr_mu_init(&state->mu);
   gpr_ref_init(&state->refcount, 1);
 
+  grpc_error **errs = gpr_malloc(sizeof(*errs) * resolved->naddrs);
   for (i = 0; i < resolved->naddrs; i++) {
-    port_temp = grpc_tcp_server_add_port(
+    errs[i] = grpc_tcp_server_add_port(
         tcp, (struct sockaddr *)&resolved->addrs[i].addr,
-        resolved->addrs[i].len);
-    if (port_temp > 0) {
+        resolved->addrs[i].len, &port_temp);
+    if (errs[i] != GRPC_ERROR_NONE) {
       if (port_num == -1) {
         port_num = port_temp;
       } else {
@@ -247,6 +249,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
 
 /* Error path: cleanup and return */
 error:
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
   if (resolved) {
     grpc_resolved_addresses_destroy(resolved);
   }
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index a59452fc5921dbdb323ff8ac0d438be1acd05dcb..fa421588e2635bae99de8b6904e40dad5eeb3860 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -59,8 +59,7 @@ typedef struct {
   gpr_timespec deadline;
   int have_read_byte;
   const grpc_httpcli_handshaker *handshaker;
-  grpc_httpcli_response_cb on_response;
-  void *user_data;
+  grpc_closure *on_done;
   grpc_httpcli_context *context;
   grpc_pollset *pollset;
   grpc_iomgr_object iomgr_obj;
@@ -96,11 +95,10 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context) {
 static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req);
 
 static void finish(grpc_exec_ctx *exec_ctx, internal_request *req,
-                   int success) {
+                   grpc_error *error) {
   grpc_pollset_set_del_pollset(exec_ctx, req->context->pollset_set,
                                req->pollset);
-  req->on_response(exec_ctx, req->user_data,
-                   success ? &req->parser.http.response : NULL);
+  grpc_exec_ctx_push(exec_ctx, req->on_done, error, NULL);
   grpc_http_parser_destroy(&req->parser);
   if (req->addresses != NULL) {
     grpc_resolved_addresses_destroy(req->addresses);
@@ -141,11 +139,11 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *user_data,
   } else if (!req->have_read_byte) {
     next_address(exec_ctx, req);
   } else {
-    int parse_success = grpc_http_parser_eof(&req->parser);
-    if (parse_success && (req->parser.type != GRPC_HTTP_RESPONSE)) {
-      parse_success = 0;
+    grpc_error *err = grpc_http_parser_eof(&req->parser);
+    if (err == GRPC_ERROR_NONE && (req->parser.type != GRPC_HTTP_RESPONSE)) {
+      err = GRPC_ERROR_CREATE("Expected http response, got http request");
     }
-    finish(exec_ctx, req, parse_success);
+    finish(exec_ctx, req, err);
   }
 }
 
@@ -208,29 +206,28 @@ static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) {
       (struct sockaddr *)&addr->addr, addr->len, req->deadline);
 }
 
-static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
-                        grpc_resolved_addresses *addresses) {
+static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   internal_request *req = arg;
-  if (!addresses) {
-    finish(exec_ctx, req, 0);
+  if (error != GRPC_ERROR_NONE) {
+    finish(exec_ctx, req, error);
     return;
   }
-  req->addresses = addresses;
   req->next_address = 0;
   next_address(exec_ctx, req);
 }
 
-static void internal_request_begin(
-    grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
-    grpc_pollset *pollset, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data, const char *name, gpr_slice request_text) {
+static void internal_request_begin(grpc_exec_ctx *exec_ctx,
+                                   grpc_httpcli_context *context,
+                                   grpc_pollset *pollset,
+                                   const grpc_httpcli_request *request,
+                                   gpr_timespec deadline, grpc_closure *on_done,
+                                   grpc_httpcli_response *response,
+                                   const char *name, gpr_slice request_text) {
   internal_request *req = gpr_malloc(sizeof(internal_request));
   memset(req, 0, sizeof(*req));
   req->request_text = request_text;
-  grpc_http_parser_init(&req->parser);
-  req->on_response = on_response;
-  req->user_data = user_data;
+  grpc_http_parser_init(&req->parser, GRPC_HTTP_RESPONSE, response);
+  req->on_done = on_done;
   req->deadline = deadline;
   req->handshaker =
       request->handshaker ? request->handshaker : &grpc_httpcli_plaintext;
@@ -247,22 +244,22 @@ static void internal_request_begin(
   grpc_pollset_set_add_pollset(exec_ctx, req->context->pollset_set,
                                req->pollset);
   grpc_resolve_address(exec_ctx, request->host, req->handshaker->default_port,
-                       on_resolved, req);
+                       grpc_closure_create(on_resolved, req), &req->addresses);
 }
 
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                       grpc_pollset *pollset,
                       const grpc_httpcli_request *request,
-                      gpr_timespec deadline,
-                      grpc_httpcli_response_cb on_response, void *user_data) {
+                      gpr_timespec deadline, grpc_closure *on_done,
+                      grpc_httpcli_response *response) {
   char *name;
   if (g_get_override &&
-      g_get_override(exec_ctx, request, deadline, on_response, user_data)) {
+      g_get_override(exec_ctx, request, deadline, on_done, response)) {
     return;
   }
   gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path);
-  internal_request_begin(exec_ctx, context, pollset, request, deadline,
-                         on_response, user_data, name,
+  internal_request_begin(exec_ctx, context, pollset, request, deadline, on_done,
+                         response, name,
                          grpc_httpcli_format_get_request(request));
   gpr_free(name);
 }
@@ -271,18 +268,18 @@ void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                        grpc_pollset *pollset,
                        const grpc_httpcli_request *request,
                        const char *body_bytes, size_t body_size,
-                       gpr_timespec deadline,
-                       grpc_httpcli_response_cb on_response, void *user_data) {
+                       gpr_timespec deadline, grpc_closure *on_done,
+                       grpc_httpcli_response *response) {
   char *name;
   if (g_post_override &&
       g_post_override(exec_ctx, request, body_bytes, body_size, deadline,
-                      on_response, user_data)) {
+                      on_done, response)) {
     return;
   }
   gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path);
   internal_request_begin(
-      exec_ctx, context, pollset, request, deadline, on_response, user_data,
-      name, grpc_httpcli_format_post_request(request, body_bytes, body_size));
+      exec_ctx, context, pollset, request, deadline, on_done, response, name,
+      grpc_httpcli_format_post_request(request, body_bytes, body_size));
   gpr_free(name);
 }
 
diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h
index 11a32a125c8c1072459736401b91f82cc5093b8f..2d57864a1ffb18030dfc5f4b034c9c19c818eca3 100644
--- a/src/core/lib/http/httpcli.h
+++ b/src/core/lib/http/httpcli.h
@@ -81,11 +81,6 @@ typedef struct grpc_httpcli_request {
 /* Expose the parser response type as a httpcli response too */
 typedef struct grpc_http_response grpc_httpcli_response;
 
-/* Callback for grpc_httpcli_get and grpc_httpcli_post. */
-typedef void (*grpc_httpcli_response_cb)(grpc_exec_ctx *exec_ctx,
-                                         void *user_data,
-                                         const grpc_http_response *response);
-
 void grpc_httpcli_context_init(grpc_httpcli_context *context);
 void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
 
@@ -102,8 +97,8 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                       grpc_pollset *pollset,
                       const grpc_httpcli_request *request,
-                      gpr_timespec deadline,
-                      grpc_httpcli_response_cb on_response, void *user_data);
+                      gpr_timespec deadline, grpc_closure *on_complete,
+                      grpc_httpcli_response *response);
 
 /* Asynchronously perform a HTTP POST.
    'context' specifies the http context under which to do the post
@@ -124,19 +119,19 @@ void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                        grpc_pollset *pollset,
                        const grpc_httpcli_request *request,
                        const char *body_bytes, size_t body_size,
-                       gpr_timespec deadline,
-                       grpc_httpcli_response_cb on_response, void *user_data);
+                       gpr_timespec deadline, grpc_closure *on_complete,
+                       grpc_httpcli_response *response);
 
 /* override functions return 1 if they handled the request, 0 otherwise */
 typedef int (*grpc_httpcli_get_override)(grpc_exec_ctx *exec_ctx,
                                          const grpc_httpcli_request *request,
                                          gpr_timespec deadline,
-                                         grpc_httpcli_response_cb on_response,
-                                         void *user_data);
+                                         grpc_closure *on_complete,
+                                         grpc_httpcli_response *response);
 typedef int (*grpc_httpcli_post_override)(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
     const char *body_bytes, size_t body_size, gpr_timespec deadline,
-    grpc_httpcli_response_cb on_response, void *user_data);
+    grpc_closure *on_complete, grpc_httpcli_response *response);
 
 void grpc_httpcli_set_override(grpc_httpcli_get_override get,
                                grpc_httpcli_post_override post);
diff --git a/src/core/lib/http/parser.c b/src/core/lib/http/parser.c
index a7efb5e73ead4434b0102103960674c32b532b7f..680174c8426c6ec245e0cf20e5802de181bc167c 100644
--- a/src/core/lib/http/parser.c
+++ b/src/core/lib/http/parser.c
@@ -48,37 +48,38 @@ static char *buf2str(void *buffer, size_t length) {
   return out;
 }
 
-static int handle_response_line(grpc_http_parser *parser) {
+static grpc_error *handle_response_line(grpc_http_parser *parser) {
   uint8_t *beg = parser->cur_line;
   uint8_t *cur = beg;
   uint8_t *end = beg + parser->cur_line_length;
 
-  if (cur == end || *cur++ != 'H') goto error;
-  if (cur == end || *cur++ != 'T') goto error;
-  if (cur == end || *cur++ != 'T') goto error;
-  if (cur == end || *cur++ != 'P') goto error;
-  if (cur == end || *cur++ != '/') goto error;
-  if (cur == end || *cur++ != '1') goto error;
-  if (cur == end || *cur++ != '.') goto error;
-  if (cur == end || *cur < '0' || *cur++ > '1') goto error;
-  if (cur == end || *cur++ != ' ') goto error;
-  if (cur == end || *cur < '1' || *cur++ > '9') goto error;
-  if (cur == end || *cur < '0' || *cur++ > '9') goto error;
-  if (cur == end || *cur < '0' || *cur++ > '9') goto error;
-  parser->http.response.status =
+  if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+  if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
+  if (cur == end || *cur++ != '1') return GRPC_ERROR_CREATE("Expected '1'");
+  if (cur == end || *cur++ != '.') return GRPC_ERROR_CREATE("Expected '.'");
+  if (cur == end || *cur < '0' || *cur++ > '1') {
+    return GRPC_ERROR_CREATE("Expected HTTP/1.0 or HTTP/1.1");
+  }
+  if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
+  if (cur == end || *cur < '1' || *cur++ > '9')
+    return GRPC_ERROR_CREATE("Expected status code");
+  if (cur == end || *cur < '0' || *cur++ > '9')
+    return GRPC_ERROR_CREATE("Expected status code");
+  if (cur == end || *cur < '0' || *cur++ > '9')
+    return GRPC_ERROR_CREATE("Expected status code");
+  parser->http.response->status =
       (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0');
-  if (cur == end || *cur++ != ' ') goto error;
+  if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
 
   /* we don't really care about the status code message */
 
-  return 1;
-
-error:
-  if (grpc_http1_trace) gpr_log(GPR_ERROR, "Failed parsing response line");
-  return 0;
+  return GRPC_ERROR_NONE;
 }
 
-static int handle_request_line(grpc_http_parser *parser) {
+static grpc_error *handle_request_line(grpc_http_parser *parser) {
   uint8_t *beg = parser->cur_line;
   uint8_t *cur = beg;
   uint8_t *end = beg + parser->cur_line_length;
@@ -87,83 +88,81 @@ static int handle_request_line(grpc_http_parser *parser) {
 
   while (cur != end && *cur++ != ' ')
     ;
-  if (cur == end) goto error;
-  parser->http.request.method = buf2str(beg, (size_t)(cur - beg - 1));
+  if (cur == end) return GRPC_ERROR_CREATE("No method on HTTP request line");
+  parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1));
 
   beg = cur;
   while (cur != end && *cur++ != ' ')
     ;
-  if (cur == end) goto error;
-  parser->http.request.path = buf2str(beg, (size_t)(cur - beg - 1));
-
-  if (cur == end || *cur++ != 'H') goto error;
-  if (cur == end || *cur++ != 'T') goto error;
-  if (cur == end || *cur++ != 'T') goto error;
-  if (cur == end || *cur++ != 'P') goto error;
-  if (cur == end || *cur++ != '/') goto error;
+  if (cur == end) return GRPC_ERROR_CREATE("No path on HTTP request line");
+  parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1));
+
+  if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+  if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
   vers_major = (uint8_t)(*cur++ - '1' + 1);
   ++cur;
-  if (cur == end) goto error;
+  if (cur == end)
+    return GRPC_ERROR_CREATE("End of line in HTTP version string");
   vers_minor = (uint8_t)(*cur++ - '1' + 1);
 
   if (vers_major == 1) {
     if (vers_minor == 0) {
-      parser->http.request.version = GRPC_HTTP_HTTP10;
+      parser->http.request->version = GRPC_HTTP_HTTP10;
     } else if (vers_minor == 1) {
-      parser->http.request.version = GRPC_HTTP_HTTP11;
+      parser->http.request->version = GRPC_HTTP_HTTP11;
     } else {
-      goto error;
+      return GRPC_ERROR_CREATE(
+          "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
     }
   } else if (vers_major == 2) {
     if (vers_minor == 0) {
-      parser->http.request.version = GRPC_HTTP_HTTP20;
+      parser->http.request->version = GRPC_HTTP_HTTP20;
     } else {
-      goto error;
+      return GRPC_ERROR_CREATE(
+          "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
     }
   } else {
-    goto error;
+    return GRPC_ERROR_CREATE("Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
   }
 
-  return 1;
-
-error:
-  if (grpc_http1_trace) gpr_log(GPR_ERROR, "Failed parsing request line");
-  return 0;
+  return GRPC_ERROR_NONE;
 }
 
-static int handle_first_line(grpc_http_parser *parser) {
-  if (parser->cur_line[0] == 'H') {
-    parser->type = GRPC_HTTP_RESPONSE;
-    return handle_response_line(parser);
-  } else {
-    parser->type = GRPC_HTTP_REQUEST;
-    return handle_request_line(parser);
+static grpc_error *handle_first_line(grpc_http_parser *parser) {
+  switch (parser->type) {
+    case GRPC_HTTP_REQUEST:
+      return handle_request_line(parser);
+    case GRPC_HTTP_RESPONSE:
+      return handle_response_line(parser);
   }
+  GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
 }
 
-static int add_header(grpc_http_parser *parser) {
+static grpc_error *add_header(grpc_http_parser *parser) {
   uint8_t *beg = parser->cur_line;
   uint8_t *cur = beg;
   uint8_t *end = beg + parser->cur_line_length;
   size_t *hdr_count = NULL;
   grpc_http_header **hdrs = NULL;
   grpc_http_header hdr = {NULL, NULL};
+  grpc_error *error = GRPC_ERROR_NONE;
 
   GPR_ASSERT(cur != end);
 
   if (*cur == ' ' || *cur == '\t') {
-    if (grpc_http1_trace)
-      gpr_log(GPR_ERROR, "Continued header lines not supported yet");
-    goto error;
+    error = GRPC_ERROR_CREATE("Continued header lines not supported yet");
+    goto done;
   }
 
   while (cur != end && *cur != ':') {
     cur++;
   }
   if (cur == end) {
-    if (grpc_http1_trace)
-      gpr_log(GPR_ERROR, "Didn't find ':' in header string");
-    goto error;
+    error = GRPC_ERROR_CREATE("Didn't find ':' in header string");
+    goto done;
   }
   GPR_ASSERT(cur >= beg);
   hdr.key = buf2str(beg, (size_t)(cur - beg));
@@ -176,11 +175,11 @@ static int add_header(grpc_http_parser *parser) {
   hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length);
 
   if (parser->type == GRPC_HTTP_RESPONSE) {
-    hdr_count = &parser->http.response.hdr_count;
-    hdrs = &parser->http.response.hdrs;
+    hdr_count = &parser->http.response->hdr_count;
+    hdrs = &parser->http.response->hdrs;
   } else if (parser->type == GRPC_HTTP_REQUEST) {
-    hdr_count = &parser->http.request.hdr_count;
-    hdrs = &parser->http.request.hdrs;
+    hdr_count = &parser->http.request->hdr_count;
+    hdrs = &parser->http.request->hdrs;
   } else {
     return 0;
   }
@@ -191,20 +190,21 @@ static int add_header(grpc_http_parser *parser) {
     *hdrs = gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs));
   }
   (*hdrs)[(*hdr_count)++] = hdr;
-  return 1;
 
-error:
-  gpr_free(hdr.key);
-  gpr_free(hdr.value);
-  return 0;
+done:
+  if (error != GRPC_ERROR_NONE) {
+    gpr_free(hdr.key);
+    gpr_free(hdr.value);
+  }
+  return error;
 }
 
-static int finish_line(grpc_http_parser *parser) {
+static grpc_error *finish_line(grpc_http_parser *parser) {
+  grpc_error *err;
   switch (parser->state) {
     case GRPC_HTTP_FIRST_LINE:
-      if (!handle_first_line(parser)) {
-        return 0;
-      }
+      err = handle_first_line(parser);
+      if (err != GRPC_ERROR_NONE) return err;
       parser->state = GRPC_HTTP_HEADERS;
       break;
     case GRPC_HTTP_HEADERS:
@@ -221,21 +221,21 @@ static int finish_line(grpc_http_parser *parser) {
   }
 
   parser->cur_line_length = 0;
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-static int addbyte_body(grpc_http_parser *parser, uint8_t byte) {
+static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) {
   size_t *body_length = NULL;
   char **body = NULL;
 
   if (parser->type == GRPC_HTTP_RESPONSE) {
-    body_length = &parser->http.response.body_length;
-    body = &parser->http.response.body;
+    body_length = &parser->http.response->body_length;
+    body = &parser->http.response->body;
   } else if (parser->type == GRPC_HTTP_REQUEST) {
-    body_length = &parser->http.request.body_length;
-    body = &parser->http.request.body;
+    body_length = &parser->http.request->body_length;
+    body = &parser->http.request->body;
   } else {
-    return 0;
+    GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
   }
 
   if (*body_length == parser->body_capacity) {
@@ -245,34 +245,35 @@ static int addbyte_body(grpc_http_parser *parser, uint8_t byte) {
   (*body)[*body_length] = (char)byte;
   (*body_length)++;
 
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-static int check_line(grpc_http_parser *parser) {
+static grpc_error *check_line(grpc_http_parser *parser) {
   if (parser->cur_line_length >= 2 &&
       parser->cur_line[parser->cur_line_length - 2] == '\r' &&
       parser->cur_line[parser->cur_line_length - 1] == '\n') {
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   // HTTP request with \n\r line termiantors.
   else if (parser->cur_line_length >= 2 &&
            parser->cur_line[parser->cur_line_length - 2] == '\n' &&
            parser->cur_line[parser->cur_line_length - 1] == '\r') {
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   // HTTP request with only \n line terminators.
   else if (parser->cur_line_length >= 1 &&
            parser->cur_line[parser->cur_line_length - 1] == '\n') {
     parser->cur_line_end_length = 1;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
-  return 0;
+  return GRPC_ERROR_CREATE(
+      "Expected line ending (one of \\r\\n, \\n\\r, or \\n)");
 }
 
-static int addbyte(grpc_http_parser *parser, uint8_t byte) {
+static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) {
   switch (parser->state) {
     case GRPC_HTTP_FIRST_LINE:
     case GRPC_HTTP_HEADERS:
@@ -287,7 +288,7 @@ static int addbyte(grpc_http_parser *parser, uint8_t byte) {
       if (check_line(parser)) {
         return finish_line(parser);
       } else {
-        return 1;
+        return GRPC_ERROR_NONE;
       }
       GPR_UNREACHABLE_CODE(return 0);
     case GRPC_HTTP_BODY:
@@ -296,46 +297,53 @@ static int addbyte(grpc_http_parser *parser, uint8_t byte) {
   GPR_UNREACHABLE_CODE(return 0);
 }
 
-void grpc_http_parser_init(grpc_http_parser *parser) {
+void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,
+                           void *request_or_response) {
   memset(parser, 0, sizeof(*parser));
   parser->state = GRPC_HTTP_FIRST_LINE;
-  parser->type = GRPC_HTTP_UNKNOWN;
+  parser->type = type;
+  parser->http.request_or_response = request_or_response;
   parser->cur_line_end_length = 2;
 }
 
-void grpc_http_parser_destroy(grpc_http_parser *parser) {
+void grpc_http_parser_destroy(grpc_http_parser *parser) {}
+
+void grpc_http_request_destroy(grpc_http_request *request) {
   size_t i;
-  if (parser->type == GRPC_HTTP_RESPONSE) {
-    gpr_free(parser->http.response.body);
-    for (i = 0; i < parser->http.response.hdr_count; i++) {
-      gpr_free(parser->http.response.hdrs[i].key);
-      gpr_free(parser->http.response.hdrs[i].value);
-    }
-    gpr_free(parser->http.response.hdrs);
-  } else if (parser->type == GRPC_HTTP_REQUEST) {
-    gpr_free(parser->http.request.body);
-    for (i = 0; i < parser->http.request.hdr_count; i++) {
-      gpr_free(parser->http.request.hdrs[i].key);
-      gpr_free(parser->http.request.hdrs[i].value);
-    }
-    gpr_free(parser->http.request.hdrs);
-    gpr_free(parser->http.request.method);
-    gpr_free(parser->http.request.path);
+  gpr_free(request->body);
+  for (i = 0; i < request->hdr_count; i++) {
+    gpr_free(request->hdrs[i].key);
+    gpr_free(request->hdrs[i].value);
   }
+  gpr_free(request->hdrs);
+  gpr_free(request->method);
+  gpr_free(request->path);
 }
 
-int grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) {
+void grpc_http_response_destroy(grpc_http_response *response) {
+  size_t i;
+  gpr_free(response->body);
+  for (i = 0; i < response->hdr_count; i++) {
+    gpr_free(response->hdrs[i].key);
+    gpr_free(response->hdrs[i].value);
+  }
+  gpr_free(response->hdrs);
+}
+
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) {
   size_t i;
 
   for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) {
-    if (!addbyte(parser, GPR_SLICE_START_PTR(slice)[i])) {
-      return 0;
-    }
+    grpc_error *err = addbyte(parser, GPR_SLICE_START_PTR(slice)[i]);
+    if (err != GRPC_ERROR_NONE) return err;
   }
 
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-int grpc_http_parser_eof(grpc_http_parser *parser) {
-  return parser->state == GRPC_HTTP_BODY;
+grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) {
+  if (parser->state != GRPC_HTTP_BODY) {
+    return GRPC_ERROR_CREATE("Did not finish headers");
+  }
+  return GRPC_ERROR_NONE;
 }
diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h
index 536637e9a2d99f52eafbb80ca252afb6b488c6c8..6df3cc8b134784917021b68832e4774b29bd1f94 100644
--- a/src/core/lib/http/parser.h
+++ b/src/core/lib/http/parser.h
@@ -36,6 +36,7 @@
 
 #include <grpc/support/port_platform.h>
 #include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/error.h"
 
 /* Maximum length of a header string of the form 'Key: Value\r\n' */
 #define GRPC_HTTP_PARSER_MAX_HEADER_LENGTH 4096
@@ -61,7 +62,6 @@ typedef enum {
 typedef enum {
   GRPC_HTTP_RESPONSE,
   GRPC_HTTP_REQUEST,
-  GRPC_HTTP_UNKNOWN
 } grpc_http_type;
 
 /* A request */
@@ -97,8 +97,9 @@ typedef struct {
   grpc_http_type type;
 
   union {
-    grpc_http_response response;
-    grpc_http_request request;
+    grpc_http_response *response;
+    grpc_http_request *request;
+    void *request_or_response;
   } http;
   size_t body_capacity;
   size_t hdr_capacity;
@@ -108,11 +109,15 @@ typedef struct {
   size_t cur_line_end_length;
 } grpc_http_parser;
 
-void grpc_http_parser_init(grpc_http_parser *parser);
+void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,
+                           void *request_or_response);
 void grpc_http_parser_destroy(grpc_http_parser *parser);
 
-int grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice);
-int grpc_http_parser_eof(grpc_http_parser *parser);
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice);
+grpc_error *grpc_http_parser_eof(grpc_http_parser *parser);
+
+void grpc_http_request_destroy(grpc_http_request *request);
+void grpc_http_response_destroy(grpc_http_response *response);
 
 extern int grpc_http1_trace;
 
diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h
index ef198fe0f664667a96c6afe2c92c12c5d35481d0..ddbe37575556ccdbf972538a4e582df5331cc59c 100644
--- a/src/core/lib/iomgr/resolve_address.h
+++ b/src/core/lib/iomgr/resolve_address.h
@@ -50,24 +50,20 @@ typedef struct {
   grpc_resolved_address *addrs;
 } grpc_resolved_addresses;
 
-/* Async result callback:
-   On success: addresses is the result, and the callee must call
-   grpc_resolved_addresses_destroy when it's done with them
-   On failure: addresses is NULL */
-typedef void (*grpc_resolve_cb)(grpc_exec_ctx *exec_ctx, void *arg,
-                                grpc_resolved_addresses *addresses);
 /* Asynchronously resolve addr. Use default_port if a port isn't designated
    in addr, otherwise use the port in addr. */
 /* TODO(ctiller): add a timeout here */
 extern void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *addr,
                                     const char *default_port,
-                                    grpc_resolve_cb cb, void *arg);
+                                    grpc_closure *on_done,
+                                    grpc_resolved_addresses **addresses);
 /* Destroy resolved addresses */
 void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addresses);
 
 /* Resolve addr in a blocking fashion. Returns NULL on failure. On success,
    result must be freed with grpc_resolved_addresses_destroy. */
-extern grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
-    const char *name, const char *default_port);
+extern grpc_error *(*grpc_blocking_resolve_address)(
+    const char *name, const char *default_port,
+    grpc_resolved_addresses **addresses);
 
 #endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */
diff --git a/src/core/lib/iomgr/resolve_address_posix.c b/src/core/lib/iomgr/resolve_address_posix.c
index e7d90b9c60d61295e19c32f957e201a2be7bbac9..7622c3e7dc470b2865858b38857adf3b89fef237 100644
--- a/src/core/lib/iomgr/resolve_address_posix.c
+++ b/src/core/lib/iomgr/resolve_address_posix.c
@@ -54,38 +54,33 @@
 #include "src/core/lib/support/block_annotate.h"
 #include "src/core/lib/support/string.h"
 
-typedef struct {
-  char *name;
-  char *default_port;
-  grpc_resolve_cb cb;
-  grpc_closure request_closure;
-  void *arg;
-} request;
-
-static grpc_resolved_addresses *blocking_resolve_address_impl(
-    const char *name, const char *default_port) {
+static grpc_error *blocking_resolve_address_impl(
+    const char *name, const char *default_port,
+    grpc_resolved_addresses **addresses) {
   struct addrinfo hints;
   struct addrinfo *result = NULL, *resp;
   char *host;
   char *port;
   int s;
   size_t i;
-  grpc_resolved_addresses *addrs = NULL;
+  grpc_error *err;
 
   if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' &&
       name[4] == ':' && name[5] != 0) {
-    return grpc_resolve_unix_domain_address(name + 5);
+    return grpc_resolve_unix_domain_address(name + 5, addresses);
   }
 
   /* parse name, splitting it into host and port parts */
   gpr_split_host_port(name, &host, &port);
   if (host == NULL) {
-    gpr_log(GPR_ERROR, "unparseable host:port: '%s'", name);
+    err = grpc_error_set_str(GRPC_ERROR_CREATE("unparseable host:port"),
+                             GRPC_ERROR_STR_TARGET_ADDRESS, name);
     goto done;
   }
   if (port == NULL) {
     if (default_port == NULL) {
-      gpr_log(GPR_ERROR, "no port in name '%s'", name);
+      err = grpc_error_set_str(GRPC_ERROR_CREATE("no port in name"),
+                               GRPC_ERROR_STR_TARGET_ADDRESS, name);
       goto done;
     }
     port = gpr_strdup(default_port);
@@ -115,23 +110,31 @@ static grpc_resolved_addresses *blocking_resolve_address_impl(
   }
 
   if (s != 0) {
-    gpr_log(GPR_ERROR, "getaddrinfo: %s", gai_strerror(s));
+    err = grpc_error_set_str(
+        grpc_error_set_str(
+            grpc_error_set_str(grpc_error_set_int(GRPC_ERROR_CREATE("OS Error"),
+                                                  GRPC_ERROR_INT_ERRNO, s),
+                               GRPC_ERROR_STR_OS_ERROR, gai_strerror(s)),
+            GRPC_ERROR_STR_SYSCALL, "getaddrinfo"),
+        GRPC_ERROR_STR_TARGET_ADDRESS, name);
     goto done;
   }
 
   /* Success path: set addrs non-NULL, fill it in */
-  addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
-  addrs->naddrs = 0;
+  *addresses = gpr_malloc(sizeof(grpc_resolved_addresses));
+  (*addresses)->naddrs = 0;
   for (resp = result; resp != NULL; resp = resp->ai_next) {
-    addrs->naddrs++;
+    (*addresses)->naddrs++;
   }
-  addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address) * addrs->naddrs);
+  (*addresses)->addrs =
+      gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs);
   i = 0;
   for (resp = result; resp != NULL; resp = resp->ai_next) {
-    memcpy(&addrs->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
-    addrs->addrs[i].len = resp->ai_addrlen;
+    memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
+    (*addresses)->addrs[i].len = resp->ai_addrlen;
     i++;
   }
+  err = GRPC_ERROR_NONE;
 
 done:
   gpr_free(host);
@@ -139,24 +142,33 @@ done:
   if (result) {
     freeaddrinfo(result);
   }
-  return addrs;
+  return err;
 }
 
-grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
-    const char *name, const char *default_port) = blocking_resolve_address_impl;
+grpc_error *(*grpc_blocking_resolve_address)(
+    const char *name, const char *default_port,
+    grpc_resolved_addresses **addresses) = blocking_resolve_address_impl;
+
+typedef struct {
+  char *name;
+  char *default_port;
+  grpc_closure *on_done;
+  grpc_resolved_addresses **addrs_out;
+  grpc_closure request_closure;
+  void *arg;
+} request;
 
 /* Callback to be passed to grpc_executor to asynch-ify
  * grpc_blocking_resolve_address */
 static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp,
                               grpc_error *error) {
   request *r = rp;
-  grpc_resolved_addresses *resolved =
-      grpc_blocking_resolve_address(r->name, r->default_port);
-  void *arg = r->arg;
-  grpc_resolve_cb cb = r->cb;
+  grpc_exec_ctx_push(
+      exec_ctx, r->on_done,
+      grpc_blocking_resolve_address(r->name, r->default_port, r->addrs_out),
+      NULL);
   gpr_free(r->name);
   gpr_free(r->default_port);
-  cb(exec_ctx, arg, resolved);
   gpr_free(r);
 }
 
@@ -166,19 +178,21 @@ void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) {
 }
 
 static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
-                                 const char *default_port, grpc_resolve_cb cb,
-                                 void *arg) {
+                                 const char *default_port,
+                                 grpc_closure *on_done,
+                                 grpc_resolved_addresses **addrs) {
   request *r = gpr_malloc(sizeof(request));
   grpc_closure_init(&r->request_closure, do_request_thread, r);
   r->name = gpr_strdup(name);
   r->default_port = gpr_strdup(default_port);
-  r->cb = cb;
-  r->arg = arg;
+  r->on_done = on_done;
+  r->addrs_out = addrs;
   grpc_executor_push(&r->request_closure, GRPC_ERROR_NONE);
 }
 
 void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
-                             const char *default_port, grpc_resolve_cb cb,
-                             void *arg) = resolve_address_impl;
+                             const char *default_port, grpc_closure *on_done,
+                             grpc_resolved_addresses **addrs) =
+    resolve_address_impl;
 
 #endif
diff --git a/src/core/lib/iomgr/unix_sockets_posix.h b/src/core/lib/iomgr/unix_sockets_posix.h
index 6758c498e5f0b0a1ec1c85bda44d25060eb5f808..db0516d94585b11128aed94c6e40848785063a3f 100644
--- a/src/core/lib/iomgr/unix_sockets_posix.h
+++ b/src/core/lib/iomgr/unix_sockets_posix.h
@@ -43,7 +43,8 @@
 
 void grpc_create_socketpair_if_unix(int sv[2]);
 
-grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name);
+grpc_error *grpc_resolve_unix_domain_address(
+    const char *name, grpc_resolved_addresses **addresses);
 
 int grpc_is_unix_socket(const struct sockaddr *addr);