Пример #1
0
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
                      const uint8_t *name, size_t namelen,
                      const uint8_t *value, size_t valuelen,
                      void *userp)
{
  struct connectdata *conn = (struct connectdata *)userp;
  struct http_conn *c = &conn->proto.httpc;
  (void)session;
  (void)frame;

  if(frame->hd.stream_id != c->stream_id) {
    return 0;
  }

  if(namelen == sizeof(":status") - 1 &&
     memcmp(STATUS, name, namelen) == 0) {
    snprintf(c->header_recvbuf->buffer, 13, "HTTP/2.0 %s", value);
    c->header_recvbuf->buffer[12] = '\r';
    return 0;
  }
  else {
    /* convert to a HTTP1-style header */
    infof(conn->data, "got header\n");
    Curl_add_buffer(c->header_recvbuf, name, namelen);
    Curl_add_buffer(c->header_recvbuf, ":", 1);
    Curl_add_buffer(c->header_recvbuf, value, valuelen);
    Curl_add_buffer(c->header_recvbuf, "\r\n", 2);
  }

  return 0; /* 0 is successful */
}
Пример #2
0
CURLcode Curl_http2_setup(struct connectdata *conn)
{
  struct http_conn *httpc = &conn->proto.httpc;
  if(conn->handler->flags & PROTOPT_SSL)
    conn->handler = &Curl_handler_http2_ssl;
  else
    conn->handler = &Curl_handler_http2;

  infof(conn->data, "Using HTTP2\n");
  httpc->bodystarted = FALSE;
  httpc->closed = FALSE;
  httpc->header_recvbuf = Curl_add_buffer_init();
  httpc->nread_header_recvbuf = 0;
  httpc->data = NULL;
  httpc->datalen = 0;
  httpc->upload_left = 0;
  httpc->upload_mem = NULL;
  httpc->upload_len = 0;
  httpc->stream_id = -1;

  conn->httpversion = 20;

  /* Put place holder for status line */
  return Curl_add_buffer(httpc->header_recvbuf, "HTTP/2.0 200\r\n", 14);
}
Пример #3
0
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                         void *userp)
{
  struct connectdata *conn = (struct connectdata *)userp;
  struct http_conn *c = &conn->proto.httpc;
  int rv;
  (void)session;
  (void)frame;
  infof(conn->data, "on_frame_recv() was called with header %x\n",
        frame->hd.type);
  switch(frame->hd.type) {
  case NGHTTP2_HEADERS:
    if(frame->headers.cat != NGHTTP2_HCAT_RESPONSE)
      break;
    c->bodystarted = TRUE;
    Curl_add_buffer(c->header_recvbuf, "\r\n", 2);
    c->nread_header_recvbuf = c->len < c->header_recvbuf->size_used ?
      c->len : c->header_recvbuf->size_used;

    memcpy(c->mem, c->header_recvbuf->buffer, c->nread_header_recvbuf);

    c->mem += c->nread_header_recvbuf;
    c->len -= c->nread_header_recvbuf;
    break;
  case NGHTTP2_PUSH_PROMISE:
    rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                   frame->hd.stream_id, NGHTTP2_CANCEL);
    if(nghttp2_is_fatal(rv)) {
      return rv;
    }
    break;
  }
  return 0;
}
Пример #4
0
Файл: http2.c Проект: RexSi/curl
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
                     const uint8_t *name, size_t namelen,
                     const uint8_t *value, size_t valuelen,
                     uint8_t flags,
                     void *userp)
{
  struct connectdata *conn = (struct connectdata *)userp;
  struct http_conn *c = &conn->proto.httpc;
  int rv;

  (void)session;
  (void)frame;
  (void)flags;

  if(frame->hd.stream_id != c->stream_id) {
    return 0;
  }

  if(c->bodystarted) {
    /* Ignore trailer or HEADERS not mapped to HTTP semantics.  The
       consequence is handled in on_frame_recv(). */
    return 0;
  }

  if(!nghttp2_check_header_name(name, namelen) ||
     !nghttp2_check_header_value(value, valuelen)) {

    rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                   frame->hd.stream_id,
                                   NGHTTP2_PROTOCOL_ERROR);

    if(nghttp2_is_fatal(rv)) {
      return NGHTTP2_ERR_CALLBACK_FAILURE;
    }

    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
  }

  if(namelen == sizeof(":status") - 1 &&
     memcmp(STATUS, name, namelen) == 0) {

    /* :status must appear exactly once. */
    if(c->status_code != -1 ||
       (c->status_code = decode_status_code(value, valuelen)) == -1) {

      rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                     frame->hd.stream_id,
                                     NGHTTP2_PROTOCOL_ERROR);
      if(nghttp2_is_fatal(rv)) {
        return NGHTTP2_ERR_CALLBACK_FAILURE;
      }

      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
    }

    Curl_add_buffer(c->header_recvbuf, "HTTP/2.0 ", 9);
    Curl_add_buffer(c->header_recvbuf, value, valuelen);
    Curl_add_buffer(c->header_recvbuf, "\r\n", 2);

    return 0;
  }
  else {
    /* Here we are sure that namelen > 0 because of
       nghttp2_check_header_name().  Pseudo header other than :status
       is illegal. */
    if(c->status_code == -1 || name[0] == ':') {
      rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                     frame->hd.stream_id,
                                     NGHTTP2_PROTOCOL_ERROR);
      if(nghttp2_is_fatal(rv)) {
        return NGHTTP2_ERR_CALLBACK_FAILURE;
      }

      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
    }

    /* convert to a HTTP1-style header */
    infof(conn->data, "got header\n");
    Curl_add_buffer(c->header_recvbuf, name, namelen);
    Curl_add_buffer(c->header_recvbuf, ":", 1);
    Curl_add_buffer(c->header_recvbuf, value, valuelen);
    Curl_add_buffer(c->header_recvbuf, "\r\n", 2);
  }

  return 0; /* 0 is successful */
}
Пример #5
0
Файл: http2.c Проект: RexSi/curl
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                         void *userp)
{
  struct connectdata *conn = (struct connectdata *)userp;
  struct http_conn *c = &conn->proto.httpc;
  int rv;
  size_t left, ncopy;

  (void)session;
  (void)frame;
  infof(conn->data, "on_frame_recv() was called with header %x\n",
        frame->hd.type);
  switch(frame->hd.type) {
  case NGHTTP2_DATA:
    /* If body started, then receiving DATA is illegal. */
    if(!c->bodystarted) {
      rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                     frame->hd.stream_id,
                                     NGHTTP2_PROTOCOL_ERROR);

      if(nghttp2_is_fatal(rv)) {
        return NGHTTP2_ERR_CALLBACK_FAILURE;
      }
    }
    break;
  case NGHTTP2_HEADERS:
    if(frame->headers.cat == NGHTTP2_HCAT_REQUEST)
      break;

    if(c->bodystarted) {
      /* Only valid HEADERS after body started is trailer header,
         which is not fully supported in this code.  If HEADERS is not
         trailer, then it is a PROTOCOL_ERROR. */
      if((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
        rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                       frame->hd.stream_id,
                                       NGHTTP2_PROTOCOL_ERROR);

        if(nghttp2_is_fatal(rv)) {
          return NGHTTP2_ERR_CALLBACK_FAILURE;
        }
      }
      break;
    }

    if(c->status_code == -1) {
      /* No :status header field means PROTOCOL_ERROR. */
      rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                     frame->hd.stream_id,
                                     NGHTTP2_PROTOCOL_ERROR);

      if(nghttp2_is_fatal(rv)) {
        return NGHTTP2_ERR_CALLBACK_FAILURE;
      }

      break;
    }

    /* Only final status code signals the end of header */
    if(c->status_code / 100 != 1) {
      c->bodystarted = TRUE;
    }

    c->status_code = -1;

    Curl_add_buffer(c->header_recvbuf, "\r\n", 2);

    left = c->header_recvbuf->size_used - c->nread_header_recvbuf;
    ncopy = c->len < left ? c->len : left;

    memcpy(c->mem, c->header_recvbuf->buffer + c->nread_header_recvbuf, ncopy);
    c->nread_header_recvbuf += ncopy;

    c->mem += ncopy;
    c->len -= ncopy;
    break;
  case NGHTTP2_PUSH_PROMISE:
    rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                   frame->hd.stream_id, NGHTTP2_CANCEL);
    if(nghttp2_is_fatal(rv)) {
      return rv;
    }
    break;
  }
  return 0;
}
Пример #6
0
int Curl_http2_switched(struct connectdata *conn)
{
  int rv;
  CURLcode rc;
  struct http_conn *httpc = &conn->proto.httpc;
  /* we are switched! */
  /* Don't know this is needed here at this moment. Original
     handler->flags is still useful. */
  if(conn->handler->flags & PROTOPT_SSL)
    conn->handler = &Curl_handler_http2_ssl;
  else
    conn->handler = &Curl_handler_http2;

  httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
  httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
  conn->recv[FIRSTSOCKET] = http2_recv;
  conn->send[FIRSTSOCKET] = http2_send;
  infof(conn->data, "We have switched to HTTP2\n");
  httpc->bodystarted = FALSE;
  httpc->closed = FALSE;
  httpc->header_recvbuf = Curl_add_buffer_init();
  httpc->nread_header_recvbuf = 0;
  httpc->data = NULL;
  httpc->datalen = 0;
  httpc->upload_left = 0;
  httpc->upload_mem = NULL;
  httpc->upload_len = 0;

  conn->httpversion = 20;

  /* Put place holder for status line */
  Curl_add_buffer(httpc->header_recvbuf, "HTTP/2.0 200\r\n", 14);

  /* TODO: May get CURLE_AGAIN */
  rv = (int) ((Curl_send*)httpc->send_underlying)
    (conn, FIRSTSOCKET,
     NGHTTP2_CLIENT_CONNECTION_HEADER,
     NGHTTP2_CLIENT_CONNECTION_HEADER_LEN,
     &rc);
  assert(rv == 24);
  if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
    /* stream 1 is opened implicitly on upgrade */
    httpc->stream_id = 1;
    /* queue SETTINGS frame (again) */
    rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
                                 httpc->binlen, NULL);
    if(rv != 0) {
      failf(conn->data, "nghttp2_session_upgrade() failed: %s(%d)",
            nghttp2_strerror(rv), rv);
      return -1;
    }
  }
  else {
    /* stream ID is unknown at this point */
    httpc->stream_id = -1;
    rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0);
    if(rv != 0) {
      failf(conn->data, "nghttp2_submit_settings() failed: %s(%d)",
            nghttp2_strerror(rv), rv);
      return -1;
    }
  }
  return 0;
}
Пример #7
0
static CURLcode rtsp_do(struct connectdata *conn, bool *done)
{
  struct Curl_easy *data = conn->data;
  CURLcode result = CURLE_OK;
  Curl_RtspReq rtspreq = data->set.rtspreq;
  struct RTSP *rtsp = data->req.protop;
  struct HTTP *http;
  Curl_send_buffer *req_buffer;
  curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
  curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */

  const char *p_request = NULL;
  const char *p_session_id = NULL;
  const char *p_accept = NULL;
  const char *p_accept_encoding = NULL;
  const char *p_range = NULL;
  const char *p_referrer = NULL;
  const char *p_stream_uri = NULL;
  const char *p_transport = NULL;
  const char *p_uagent = NULL;
  const char *p_proxyuserpwd = NULL;
  const char *p_userpwd = NULL;

  *done = TRUE;

  http = &(rtsp->http_wrapper);
  /* Assert that no one has changed the RTSP struct in an evil way */
  DEBUGASSERT((void *)http == (void *)rtsp);

  rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
  rtsp->CSeq_recv = 0;

  /* Setup the 'p_request' pointer to the proper p_request string
   * Since all RTSP requests are included here, there is no need to
   * support custom requests like HTTP.
   **/
  data->set.opt_no_body = TRUE; /* most requests don't contain a body */
  switch(rtspreq) {
  default:
    failf(data, "Got invalid RTSP request");
    return CURLE_BAD_FUNCTION_ARGUMENT;
  case RTSPREQ_OPTIONS:
    p_request = "OPTIONS";
    break;
  case RTSPREQ_DESCRIBE:
    p_request = "DESCRIBE";
    data->set.opt_no_body = FALSE;
    break;
  case RTSPREQ_ANNOUNCE:
    p_request = "ANNOUNCE";
    break;
  case RTSPREQ_SETUP:
    p_request = "SETUP";
    break;
  case RTSPREQ_PLAY:
    p_request = "PLAY";
    break;
  case RTSPREQ_PAUSE:
    p_request = "PAUSE";
    break;
  case RTSPREQ_TEARDOWN:
    p_request = "TEARDOWN";
    break;
  case RTSPREQ_GET_PARAMETER:
    /* GET_PARAMETER's no_body status is determined later */
    p_request = "GET_PARAMETER";
    data->set.opt_no_body = FALSE;
    break;
  case RTSPREQ_SET_PARAMETER:
    p_request = "SET_PARAMETER";
    break;
  case RTSPREQ_RECORD:
    p_request = "RECORD";
    break;
  case RTSPREQ_RECEIVE:
    p_request = "";
    /* Treat interleaved RTP as body*/
    data->set.opt_no_body = FALSE;
    break;
  case RTSPREQ_LAST:
    failf(data, "Got invalid RTSP request: RTSPREQ_LAST");
    return CURLE_BAD_FUNCTION_ARGUMENT;
  }

  if(rtspreq == RTSPREQ_RECEIVE) {
    Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
                        &http->readbytecount, -1, NULL);

    return result;
  }

  p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
  if(!p_session_id &&
     (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
    failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
          p_request);
    return CURLE_BAD_FUNCTION_ARGUMENT;
  }

  /* TODO: proxy? */

  /* Stream URI. Default to server '*' if not specified */
  if(data->set.str[STRING_RTSP_STREAM_URI]) {
    p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
  }
  else {
    p_stream_uri = "*";
  }

  /* Transport Header for SETUP requests */
  p_transport = Curl_checkheaders(conn, "Transport:");
  if(rtspreq == RTSPREQ_SETUP && !p_transport) {
    /* New Transport: setting? */
    if(data->set.str[STRING_RTSP_TRANSPORT]) {
      Curl_safefree(conn->allocptr.rtsp_transport);

      conn->allocptr.rtsp_transport =
        aprintf("Transport: %s\r\n",
                data->set.str[STRING_RTSP_TRANSPORT]);
      if(!conn->allocptr.rtsp_transport)
        return CURLE_OUT_OF_MEMORY;
    }
    else {
      failf(data,
            "Refusing to issue an RTSP SETUP without a Transport: header.");
      return CURLE_BAD_FUNCTION_ARGUMENT;
    }

    p_transport = conn->allocptr.rtsp_transport;
  }

  /* Accept Headers for DESCRIBE requests */
  if(rtspreq == RTSPREQ_DESCRIBE) {
    /* Accept Header */
    p_accept = Curl_checkheaders(conn, "Accept:")?
      NULL:"Accept: application/sdp\r\n";

    /* Accept-Encoding header */
    if(!Curl_checkheaders(conn, "Accept-Encoding:") &&
       data->set.str[STRING_ENCODING]) {
      Curl_safefree(conn->allocptr.accept_encoding);
      conn->allocptr.accept_encoding =
        aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);

      if(!conn->allocptr.accept_encoding)
        return CURLE_OUT_OF_MEMORY;

      p_accept_encoding = conn->allocptr.accept_encoding;
    }
  }

  /* The User-Agent string might have been allocated in url.c already, because
     it might have been used in the proxy connect, but if we have got a header
     with the user-agent string specified, we erase the previously made string
     here. */
  if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) {
    Curl_safefree(conn->allocptr.uagent);
    conn->allocptr.uagent = NULL;
  }
  else if(!Curl_checkheaders(conn, "User-Agent:") &&
          data->set.str[STRING_USERAGENT]) {
    p_uagent = conn->allocptr.uagent;
  }

  /* setup the authentication headers */
  result = Curl_http_output_auth(conn, p_request, p_stream_uri, FALSE);
  if(result)
    return result;

  p_proxyuserpwd = conn->allocptr.proxyuserpwd;
  p_userpwd = conn->allocptr.userpwd;

  /* Referrer */
  Curl_safefree(conn->allocptr.ref);
  if(data->change.referer && !Curl_checkheaders(conn, "Referer:"))
    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
  else
    conn->allocptr.ref = NULL;

  p_referrer = conn->allocptr.ref;

  /*
   * Range Header
   * Only applies to PLAY, PAUSE, RECORD
   *
   * Go ahead and use the Range stuff supplied for HTTP
   */
  if(data->state.use_range &&
     (rtspreq  & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {

    /* Check to see if there is a range set in the custom headers */
    if(!Curl_checkheaders(conn, "Range:") && data->state.range) {
      Curl_safefree(conn->allocptr.rangeline);
      conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
      p_range = conn->allocptr.rangeline;
    }
  }

  /*
   * Sanity check the custom headers
   */
  if(Curl_checkheaders(conn, "CSeq:")) {
    failf(data, "CSeq cannot be set as a custom header.");
    return CURLE_RTSP_CSEQ_ERROR;
  }
  if(Curl_checkheaders(conn, "Session:")) {
    failf(data, "Session ID cannot be set as a custom header.");
    return CURLE_BAD_FUNCTION_ARGUMENT;
  }

  /* Initialize a dynamic send buffer */
  req_buffer = Curl_add_buffer_init();

  if(!req_buffer)
    return CURLE_OUT_OF_MEMORY;

  result =
    Curl_add_bufferf(req_buffer,
                     "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
                     "CSeq: %ld\r\n", /* CSeq */
                     p_request, p_stream_uri, rtsp->CSeq_sent);
  if(result)
    return result;

  /*
   * Rather than do a normal alloc line, keep the session_id unformatted
   * to make comparison easier
   */
  if(p_session_id) {
    result = Curl_add_bufferf(req_buffer, "Session: %s\r\n", p_session_id);
    if(result)
      return result;
  }

  /*
   * Shared HTTP-like options
   */
  result = Curl_add_bufferf(req_buffer,
                            "%s" /* transport */
                            "%s" /* accept */
                            "%s" /* accept-encoding */
                            "%s" /* range */
                            "%s" /* referrer */
                            "%s" /* user-agent */
                            "%s" /* proxyuserpwd */
                            "%s" /* userpwd */
                            ,
                            p_transport ? p_transport : "",
                            p_accept ? p_accept : "",
                            p_accept_encoding ? p_accept_encoding : "",
                            p_range ? p_range : "",
                            p_referrer ? p_referrer : "",
                            p_uagent ? p_uagent : "",
                            p_proxyuserpwd ? p_proxyuserpwd : "",
                            p_userpwd ? p_userpwd : "");

  /*
   * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
   * with basic and digest, it will be freed anyway by the next request
   */
  Curl_safefree(conn->allocptr.userpwd);
  conn->allocptr.userpwd = NULL;

  if(result)
    return result;

  if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
    result = Curl_add_timecondition(data, req_buffer);
    if(result)
      return result;
  }

  result = Curl_add_custom_headers(conn, FALSE, req_buffer);
  if(result)
    return result;

  if(rtspreq == RTSPREQ_ANNOUNCE ||
     rtspreq == RTSPREQ_SET_PARAMETER ||
     rtspreq == RTSPREQ_GET_PARAMETER) {

    if(data->set.upload) {
      putsize = data->state.infilesize;
      data->set.httpreq = HTTPREQ_PUT;

    }
    else {
      postsize = (data->state.infilesize != -1)?
        data->state.infilesize:
        (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
      data->set.httpreq = HTTPREQ_POST;
    }

    if(putsize > 0 || postsize > 0) {
      /* As stated in the http comments, it is probably not wise to
       * actually set a custom Content-Length in the headers */
      if(!Curl_checkheaders(conn, "Content-Length:")) {
        result = Curl_add_bufferf(req_buffer,
            "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
            (data->set.upload ? putsize : postsize));
        if(result)
          return result;
      }

      if(rtspreq == RTSPREQ_SET_PARAMETER ||
         rtspreq == RTSPREQ_GET_PARAMETER) {
        if(!Curl_checkheaders(conn, "Content-Type:")) {
          result = Curl_add_bufferf(req_buffer,
              "Content-Type: text/parameters\r\n");
          if(result)
            return result;
        }
      }

      if(rtspreq == RTSPREQ_ANNOUNCE) {
        if(!Curl_checkheaders(conn, "Content-Type:")) {
          result = Curl_add_bufferf(req_buffer,
              "Content-Type: application/sdp\r\n");
          if(result)
            return result;
        }
      }

      data->state.expect100header = FALSE; /* RTSP posts are simple/small */
    }
    else if(rtspreq == RTSPREQ_GET_PARAMETER) {
      /* Check for an empty GET_PARAMETER (heartbeat) request */
      data->set.httpreq = HTTPREQ_HEAD;
      data->set.opt_no_body = TRUE;
    }
  }

  /* RTSP never allows chunked transfer */
  data->req.forbidchunk = TRUE;
  /* Finish the request buffer */
  result = Curl_add_buffer(req_buffer, "\r\n", 2);
  if(result)
    return result;

  if(postsize > 0) {
    result = Curl_add_buffer(req_buffer, data->set.postfields,
                             (size_t)postsize);
    if(result)
      return result;
  }

  /* issue the request */
  result = Curl_add_buffer_send(req_buffer, conn,
                                &data->info.request_size, 0, FIRSTSOCKET);
  if(result) {
    failf(data, "Failed sending RTSP request");
    return result;
  }

  Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
                      putsize?FIRSTSOCKET:-1,
                      putsize?&http->writebytecount:NULL);

  /* Increment the CSeq on success */
  data->state.rtsp_next_client_CSeq++;

  if(http->writebytecount) {
    /* if a request-body has been sent off, we make sure this progress is
       noted properly */
    Curl_pgrsSetUploadCounter(data, http->writebytecount);
    if(Curl_pgrsUpdate(conn))
      result = CURLE_ABORTED_BY_CALLBACK;
  }

  return result;
}
Пример #8
0
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
                     const uint8_t *name, size_t namelen,
                     const uint8_t *value, size_t valuelen,
                     uint8_t flags,
                     void *userp)
{
  struct connectdata *conn = (struct connectdata *)userp;
  struct HTTP *stream;
  struct SessionHandle *data_s;
  int32_t stream_id = frame->hd.stream_id;

  (void)session;
  (void)frame;
  (void)flags;

  /* Ignore PUSH_PROMISE for now */
  if(frame->hd.type != NGHTTP2_HEADERS) {
    return 0;
  }

  DEBUGASSERT(stream_id); /* should never be a zero stream ID here */

  /* get the stream from the hash based on Stream ID */
  data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id,
                          sizeof(stream_id));
  if(!data_s) {
    /* Receiving a Stream ID not in the hash should not happen, this is an
       internal error more than anything else! */
    failf(conn->data, "Received frame on Stream ID: %x not in stream hash!",
          stream_id);
    return NGHTTP2_ERR_CALLBACK_FAILURE;
  }
  stream = data_s->req.protop;

  if(stream->bodystarted)
    /* Ignore trailer or HEADERS not mapped to HTTP semantics.  The
       consequence is handled in on_frame_recv(). */
    return 0;

  if(namelen == sizeof(":status") - 1 &&
     memcmp(":status", name, namelen) == 0) {
    /* nghttp2 guarantees :status is received first and only once, and
       value is 3 digits status code, and decode_status_code always
       succeeds. */
    stream->status_code = decode_status_code(value, valuelen);
    DEBUGASSERT(stream->status_code != -1);

    Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9);
    Curl_add_buffer(stream->header_recvbuf, value, valuelen);
    Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
    data_s->state.drain++;
    Curl_expire(data_s, 1);

    DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d\n",
                 stream->status_code));
    return 0;
  }

  /* nghttp2 guarantees that namelen > 0, and :status was already
     received, and this is not pseudo-header field . */
  /* convert to a HTTP1-style header */
  Curl_add_buffer(stream->header_recvbuf, name, namelen);
  Curl_add_buffer(stream->header_recvbuf, ":", 1);
  Curl_add_buffer(stream->header_recvbuf, value, valuelen);
  Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
  data_s->state.drain++;
  Curl_expire(data_s, 1);

  DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
               value));

  return 0; /* 0 is successful */
}
Пример #9
0
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                         void *userp)
{
  struct connectdata *conn = (struct connectdata *)userp;
  struct http_conn *httpc = &conn->proto.httpc;
  struct SessionHandle *data_s = NULL;
  struct HTTP *stream = NULL;
  int rv;
  size_t left, ncopy;
  int32_t stream_id = frame->hd.stream_id;

  (void)session;
  (void)frame;
  DEBUGF(infof(conn->data, "on_frame_recv() header %x stream %x\n",
               frame->hd.type, stream_id));

  if(stream_id) {
    /* get the stream from the hash based on Stream ID, stream ID zero is for
       connection-oriented stuff */
    data_s = Curl_hash_pick(&httpc->streamsh, &stream_id,
                            sizeof(stream_id));
    if(!data_s) {
      /* Receiving a Stream ID not in the hash should not happen, this is an
         internal error more than anything else! */
      failf(conn->data, "Received frame on Stream ID: %x not in stream hash!",
            stream_id);
      return NGHTTP2_ERR_CALLBACK_FAILURE;
    }
    stream = data_s->req.protop;
  }

  switch(frame->hd.type) {
  case NGHTTP2_DATA:
    /* If body started on this stream, then receiving DATA is illegal. */
    if(!stream->bodystarted) {
      rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                     stream_id, NGHTTP2_PROTOCOL_ERROR);

      if(nghttp2_is_fatal(rv)) {
        return NGHTTP2_ERR_CALLBACK_FAILURE;
      }
    }
    break;
  case NGHTTP2_HEADERS:
    if(frame->headers.cat == NGHTTP2_HCAT_REQUEST)
      break;

    if(stream->bodystarted) {
      /* Only valid HEADERS after body started is trailer HEADERS.  We
         ignores trailer HEADERS for now.  nghttp2 guarantees that it
         has END_STREAM flag set. */
      break;
    }

    /* nghttp2 guarantees that :status is received, and we store it to
       stream->status_code */
    DEBUGASSERT(stream->status_code != -1);

    /* Only final status code signals the end of header */
    if(stream->status_code / 100 != 1) {
      stream->bodystarted = TRUE;
      stream->status_code = -1;
    }

    Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);

    left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
    ncopy = MIN(stream->len, left);

    memcpy(&stream->mem[stream->memlen],
           stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
           ncopy);
    stream->nread_header_recvbuf += ncopy;

    DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
                 ncopy, stream_id, stream->mem));

    stream->len -= ncopy;
    stream->memlen += ncopy;

    data_s->state.drain++;
    Curl_expire(data_s, 1);
    break;
  case NGHTTP2_PUSH_PROMISE:
    DEBUGF(infof(data_s, "Got PUSH_PROMISE, RST_STREAM it!\n"));
    rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                   frame->push_promise.promised_stream_id,
                                   NGHTTP2_CANCEL);
    if(nghttp2_is_fatal(rv)) {
      return rv;
    }
    break;
  case NGHTTP2_SETTINGS:
  {
    uint32_t max_conn = httpc->settings.max_concurrent_streams;
    DEBUGF(infof(conn->data, "Got SETTINGS for stream %u!\n", stream_id));
    httpc->settings.max_concurrent_streams =
      nghttp2_session_get_remote_settings(
        session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
    httpc->settings.enable_push =
      nghttp2_session_get_remote_settings(
        session, NGHTTP2_SETTINGS_ENABLE_PUSH);
    DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
                 httpc->settings.max_concurrent_streams));
    DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
                 httpc->settings.enable_push?"TRUE":"false"));
    if(max_conn != httpc->settings.max_concurrent_streams) {
      /* only signal change if the value actually changed */
      infof(conn->data,
            "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
      Curl_multi_connchanged(conn->data->multi);
    }
  }
  break;
  default:
    DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
                 frame->hd.type, stream_id));
    break;
  }
  return 0;
}
Пример #10
0
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
                     const uint8_t *name, size_t namelen,
                     const uint8_t *value, size_t valuelen,
                     uint8_t flags,
                     void *userp)
{
  struct HTTP *stream;
  struct SessionHandle *data_s;
  int32_t stream_id = frame->hd.stream_id;

  (void)flags;
  (void)userp;

  DEBUGASSERT(stream_id); /* should never be a zero stream ID here */

  /* get the stream from the hash based on Stream ID */
  data_s = nghttp2_session_get_stream_user_data(session, stream_id);
  if(!data_s)
    /* Receiving a Stream ID not in the hash should not happen, this is an
       internal error more than anything else! */
    return NGHTTP2_ERR_CALLBACK_FAILURE;

  stream = data_s->req.protop;
  if(!stream) {
    failf(data_s, "Internal NULL stream! 5\n");
    return NGHTTP2_ERR_CALLBACK_FAILURE;
  }

  if(stream->bodystarted)
    /* Ignore trailer or HEADERS not mapped to HTTP semantics.  The
       consequence is handled in on_frame_recv(). */
    return 0;

  /* Store received PUSH_PROMISE headers to be used when the subsequent
     PUSH_PROMISE callback comes */
  if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
    char *h;

    if(!stream->push_headers) {
      stream->push_headers_alloc = 10;
      stream->push_headers = malloc(stream->push_headers_alloc *
                                    sizeof(char *));
      stream->push_headers_used = 0;
    }
    else if(stream->push_headers_used ==
            stream->push_headers_alloc) {
      char **headp;
      stream->push_headers_alloc *= 2;
      headp = realloc(stream->push_headers,
                      stream->push_headers_alloc * sizeof(char *));
      if(!headp) {
        free(stream->push_headers);
        stream->push_headers = NULL;
        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
      }
      stream->push_headers = headp;
    }
    h = aprintf("%s:%s", name, value);
    if(h)
      stream->push_headers[stream->push_headers_used++] = h;
    return 0;
  }

  if(namelen == sizeof(":status") - 1 &&
     memcmp(":status", name, namelen) == 0) {
    /* nghttp2 guarantees :status is received first and only once, and
       value is 3 digits status code, and decode_status_code always
       succeeds. */
    stream->status_code = decode_status_code(value, valuelen);
    DEBUGASSERT(stream->status_code != -1);

    Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9);
    Curl_add_buffer(stream->header_recvbuf, value, valuelen);
    Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
    data_s->state.drain++;
    Curl_expire(data_s, 1);

    DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d\n",
                 stream->status_code));
    return 0;
  }

  /* nghttp2 guarantees that namelen > 0, and :status was already
     received, and this is not pseudo-header field . */
  /* convert to a HTTP1-style header */
  Curl_add_buffer(stream->header_recvbuf, name, namelen);
  Curl_add_buffer(stream->header_recvbuf, ":", 1);
  Curl_add_buffer(stream->header_recvbuf, value, valuelen);
  Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
  data_s->state.drain++;
  Curl_expire(data_s, 1);

  DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
               value));

  return 0; /* 0 is successful */
}
Пример #11
0
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                         void *userp)
{
  struct connectdata *conn = (struct connectdata *)userp;
  struct http_conn *httpc = NULL;
  struct SessionHandle *data_s = NULL;
  struct HTTP *stream = NULL;
  static int lastStream = -1;
  int rv;
  size_t left, ncopy;
  int32_t stream_id = frame->hd.stream_id;

  if(!stream_id) {
    /* stream ID zero is for connection-oriented stuff */
    return 0;
  }
  data_s = nghttp2_session_get_stream_user_data(session,
                                                frame->hd.stream_id);
  if(lastStream != frame->hd.stream_id) {
    lastStream = frame->hd.stream_id;
  }
  if(!data_s) {
    DEBUGF(infof(conn->data,
                 "No SessionHandle associated with stream: %x\n",
                 stream_id));
    return 0;
  }

  stream = data_s->req.protop;
  if(!stream)
    return NGHTTP2_ERR_CALLBACK_FAILURE;

  DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
               frame->hd.type, stream_id));

  conn = data_s->easy_conn;
  assert(conn);
  assert(conn->data == data_s);
  httpc = &conn->proto.httpc;
  switch(frame->hd.type) {
  case NGHTTP2_DATA:
    /* If body started on this stream, then receiving DATA is illegal. */
    if(!stream->bodystarted) {
      rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                     stream_id, NGHTTP2_PROTOCOL_ERROR);

      if(nghttp2_is_fatal(rv)) {
        return NGHTTP2_ERR_CALLBACK_FAILURE;
      }
    }
    break;
  case NGHTTP2_HEADERS:
    if(frame->headers.cat == NGHTTP2_HCAT_REQUEST)
      break;

    if(stream->bodystarted) {
      /* Only valid HEADERS after body started is trailer HEADERS.  We
         ignores trailer HEADERS for now.  nghttp2 guarantees that it
         has END_STREAM flag set. */
      break;
    }

    /* nghttp2 guarantees that :status is received, and we store it to
       stream->status_code */
    DEBUGASSERT(stream->status_code != -1);

    /* Only final status code signals the end of header */
    if(stream->status_code / 100 != 1) {
      stream->bodystarted = TRUE;
      stream->status_code = -1;
    }

    Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);

    left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
    ncopy = MIN(stream->len, left);

    memcpy(&stream->mem[stream->memlen],
           stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
           ncopy);
    stream->nread_header_recvbuf += ncopy;

    DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
                 ncopy, stream_id, stream->mem));

    stream->len -= ncopy;
    stream->memlen += ncopy;

    data_s->state.drain++;
    {
      /* get the pointer from userp again since it was re-assigned above */
      struct connectdata *conn_s = (struct connectdata *)userp;

      /* if we receive data for another handle, wake that up */
      if(conn_s->data != data_s)
        Curl_expire(data_s, 1);
    }
    break;
  case NGHTTP2_PUSH_PROMISE:
    rv = push_promise(data_s, conn, &frame->push_promise);
    if(rv) { /* deny! */
      rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
                                     frame->push_promise.promised_stream_id,
                                     NGHTTP2_CANCEL);
      if(nghttp2_is_fatal(rv)) {
        return rv;
      }
    }
    break;
  case NGHTTP2_SETTINGS:
  {
    uint32_t max_conn = httpc->settings.max_concurrent_streams;
    DEBUGF(infof(conn->data, "Got SETTINGS for stream %u!\n", stream_id));
    httpc->settings.max_concurrent_streams =
      nghttp2_session_get_remote_settings(
        session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
    httpc->settings.enable_push =
      nghttp2_session_get_remote_settings(
        session, NGHTTP2_SETTINGS_ENABLE_PUSH);
    DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
                 httpc->settings.max_concurrent_streams));
    DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
                 httpc->settings.enable_push?"TRUE":"false"));
    if(max_conn != httpc->settings.max_concurrent_streams) {
      /* only signal change if the value actually changed */
      infof(conn->data,
            "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
      Curl_multi_connchanged(conn->data->multi);
    }
  }
  break;
  default:
    DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
                 frame->hd.type, stream_id));
    break;
  }
  return 0;
}