CURLcode Curl_http()

in libs/curl/lib/c-hyper.c [799:1164]


CURLcode Curl_http(struct Curl_easy *data, bool *done)
{
  struct connectdata *conn = data->conn;
  struct hyptransfer *h = &data->hyp;
  hyper_io *io = NULL;
  hyper_clientconn_options *options = NULL;
  hyper_task *task = NULL; /* for the handshake */
  hyper_task *sendtask = NULL; /* for the send */
  hyper_clientconn *client = NULL;
  hyper_request *req = NULL;
  hyper_headers *headers = NULL;
  hyper_task *handshake = NULL;
  CURLcode result;
  const char *p_accept; /* Accept: string */
  const char *method;
  Curl_HttpReq httpreq;
  const char *te = NULL; /* transfer-encoding */
  hyper_code rc;

  /* Always consider the DO phase done after this function call, even if there
     may be parts of the request that is not yet sent, since we can deal with
     the rest of the request in the PERFORM phase. */
  *done = TRUE;
  result = Curl_client_start(data);
  if(result)
    return result;

  /* Add collecting of headers written to client. For a new connection,
   * we might have done that already, but reuse
   * or multiplex needs it here as well. */
  result = Curl_headers_init(data);
  if(result)
    return result;

  infof(data, "Time for the Hyper dance");
  memset(h, 0, sizeof(struct hyptransfer));

  result = Curl_http_host(data, conn);
  if(result)
    return result;

  Curl_http_method(data, conn, &method, &httpreq);

  DEBUGASSERT(data->req.bytecount ==  0);

  /* setup the authentication headers */
  {
    char *pq = NULL;
    if(data->state.up.query) {
      pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
      if(!pq)
        return CURLE_OUT_OF_MEMORY;
    }
    result = Curl_http_output_auth(data, conn, method, httpreq,
                                   (pq ? pq : data->state.up.path), FALSE);
    free(pq);
    if(result)
      return result;
  }

  result = Curl_http_req_set_reader(data, httpreq, &te);
  if(result)
    goto error;

  result = Curl_http_range(data, httpreq);
  if(result)
    return result;

  result = Curl_http_useragent(data);
  if(result)
    return result;

  io = hyper_io_new();
  if(!io) {
    failf(data, "Couldn't create hyper IO");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }
  /* tell Hyper how to read/write network data */
  h->io_ctx.data = data;
  h->io_ctx.sockindex = FIRSTSOCKET;
  hyper_io_set_userdata(io, &h->io_ctx);
  hyper_io_set_read(io, Curl_hyper_recv);
  hyper_io_set_write(io, Curl_hyper_send);

  /* create an executor to poll futures */
  if(!h->exec) {
    h->exec = hyper_executor_new();
    if(!h->exec) {
      failf(data, "Couldn't create hyper executor");
      result = CURLE_OUT_OF_MEMORY;
      goto error;
    }
  }

  options = hyper_clientconn_options_new();
  if(!options) {
    failf(data, "Couldn't create hyper client options");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }
  if(conn->alpn == CURL_HTTP_VERSION_2) {
    failf(data, "ALPN protocol h2 not supported with Hyper");
    result = CURLE_UNSUPPORTED_PROTOCOL;
    goto error;
  }
  hyper_clientconn_options_set_preserve_header_case(options, 1);
  hyper_clientconn_options_set_preserve_header_order(options, 1);
  hyper_clientconn_options_http1_allow_multiline_headers(options, 1);

  hyper_clientconn_options_exec(options, h->exec);

  /* "Both the `io` and the `options` are consumed in this function call" */
  handshake = hyper_clientconn_handshake(io, options);
  if(!handshake) {
    failf(data, "Couldn't create hyper client handshake");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }
  io = NULL;
  options = NULL;

  if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
    failf(data, "Couldn't hyper_executor_push the handshake");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }
  handshake = NULL; /* ownership passed on */

  task = hyper_executor_poll(h->exec);
  if(!task) {
    failf(data, "Couldn't hyper_executor_poll the handshake");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }

  client = hyper_task_value(task);
  hyper_task_free(task);

  req = hyper_request_new();
  if(!req) {
    failf(data, "Couldn't hyper_request_new");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }

  if(!Curl_use_http_1_1plus(data, conn)) {
    if(HYPERE_OK != hyper_request_set_version(req,
                                              HYPER_HTTP_VERSION_1_0)) {
      failf(data, "error setting HTTP version");
      result = CURLE_OUT_OF_MEMORY;
      goto error;
    }
  }

  if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
    failf(data, "error setting method");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }

  result = request_target(data, conn, method, req);
  if(result)
    goto error;

  headers = hyper_request_headers(req);
  if(!headers) {
    failf(data, "hyper_request_headers");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }

  rc = hyper_request_on_informational(req, http1xx_cb, data);
  if(rc) {
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }

  if(data->state.aptr.host) {
    result = Curl_hyper_header(data, headers, data->state.aptr.host);
    if(result)
      goto error;
  }

#ifndef CURL_DISABLE_PROXY
  if(data->state.aptr.proxyuserpwd) {
    result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
    if(result)
      goto error;
  }
#endif

  if(data->state.aptr.userpwd) {
    result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
    if(result)
      goto error;
  }

  if((data->state.use_range && data->state.aptr.rangeline)) {
    result = Curl_hyper_header(data, headers, data->state.aptr.rangeline);
    if(result)
      goto error;
  }

  if(data->set.str[STRING_USERAGENT] &&
     *data->set.str[STRING_USERAGENT] &&
     data->state.aptr.uagent) {
    result = Curl_hyper_header(data, headers, data->state.aptr.uagent);
    if(result)
      goto error;
  }

  p_accept = Curl_checkheaders(data,
                               STRCONST("Accept"))?NULL:"Accept: */*\r\n";
  if(p_accept) {
    result = Curl_hyper_header(data, headers, p_accept);
    if(result)
      goto error;
  }
  if(te) {
    result = Curl_hyper_header(data, headers, te);
    if(result)
      goto error;
  }

#ifndef CURL_DISABLE_ALTSVC
  if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
    char *altused = aprintf("Alt-Used: %s:%d\r\n",
                            conn->conn_to_host.name, conn->conn_to_port);
    if(!altused) {
      result = CURLE_OUT_OF_MEMORY;
      goto error;
    }
    result = Curl_hyper_header(data, headers, altused);
    if(result)
      goto error;
    free(altused);
  }
#endif

#ifndef CURL_DISABLE_PROXY
  if(conn->bits.httpproxy && !conn->bits.tunnel_proxy &&
     !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
     !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
    result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive");
    if(result)
      goto error;
  }
#endif

  Curl_safefree(data->state.aptr.ref);
  if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
    data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
    if(!data->state.aptr.ref)
      result = CURLE_OUT_OF_MEMORY;
    else
      result = Curl_hyper_header(data, headers, data->state.aptr.ref);
    if(result)
      goto error;
  }

#ifdef HAVE_LIBZ
  /* we only consider transfer-encoding magic if libz support is built-in */
  result = Curl_transferencode(data);
  if(result)
    goto error;
  result = Curl_hyper_header(data, headers, data->state.aptr.te);
  if(result)
    goto error;
#endif

  if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
     data->set.str[STRING_ENCODING]) {
    Curl_safefree(data->state.aptr.accept_encoding);
    data->state.aptr.accept_encoding =
      aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
    if(!data->state.aptr.accept_encoding)
      result = CURLE_OUT_OF_MEMORY;
    else
      result = Curl_hyper_header(data, headers,
                                 data->state.aptr.accept_encoding);
    if(result)
      goto error;
  }
  else
    Curl_safefree(data->state.aptr.accept_encoding);

  result = cookies(data, conn, headers);
  if(result)
    goto error;

  if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
    result = Curl_ws_request(data, headers);

  result = Curl_add_timecondition(data, headers);
  if(result)
    goto error;

  result = Curl_add_custom_headers(data, FALSE, headers);
  if(result)
    goto error;

  result = finalize_request(data, headers, req, httpreq);
  if(result)
    goto error;

  Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);

  if(data->req.upload_chunky && data->req.authneg) {
    data->req.upload_chunky = TRUE;
  }
  else {
    data->req.upload_chunky = FALSE;
  }
  sendtask = hyper_clientconn_send(client, req);
  if(!sendtask) {
    failf(data, "hyper_clientconn_send");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }
  req = NULL;

  if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
    failf(data, "Couldn't hyper_executor_push the send");
    result = CURLE_OUT_OF_MEMORY;
    goto error;
  }
  sendtask = NULL; /* ownership passed on */

  hyper_clientconn_free(client);
  client = NULL;

  if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
    /* HTTP GET/HEAD download */
    Curl_pgrsSetUploadSize(data, 0); /* nothing */
  }

  Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
  conn->datastream = Curl_hyper_stream;

  /* clear userpwd and proxyuserpwd to avoid reusing old credentials
   * from reused connections */
  Curl_safefree(data->state.aptr.userpwd);
#ifndef CURL_DISABLE_PROXY
  Curl_safefree(data->state.aptr.proxyuserpwd);
#endif
  return CURLE_OK;
error:
  DEBUGASSERT(result);
  if(io)
    hyper_io_free(io);

  if(options)
    hyper_clientconn_options_free(options);

  if(handshake)
    hyper_task_free(handshake);

  if(client)
    hyper_clientconn_free(client);

  if(req)
    hyper_request_free(req);

  return result;
}