Beispiel #1
0
/**
 *  @return
 *   -1 if a header was invalid (see http status for details).
 *    0 if the end-of-headers was reached.
 *    1 if a header was successfully parsed.
 */
int
http_parse_header(http_t *ctx, char *buf, size_t buf_size,
    char **name_ptr, char **value_ptr)
{
  char *value;
 
  RUNTIME_ASSERT(ctx);
  RUNTIME_ASSERT(HTTP_STATE_HEADERS == ctx->state);

  if (name_ptr)
    *name_ptr = NULL;
  if (value_ptr)
    *value_ptr = NULL;
  
  switch (http_read_header(ctx, buf, buf_size)) {
  case -1:
    DBUG("http_read_header() failed: %u \"%s\"",
    ctx->status_code, ctx->status_msg);
    return -1;
    
  case 0:
    ctx->state = HTTP_STATE_BODY;
    return 0;

  case 1:
    break;

  default:
    RUNTIME_ASSERT(0);
    http_set_status(ctx, 500, "Internal Server Error");
    return -1;
  }

  value = http_parse_header_name(buf);
  if (!value) {
    http_set_status(ctx, 400, "Bad Request (Bad Header)");
    return -1;
  }

  if (name_ptr)
    *name_ptr = buf;
  
  if (value_ptr)
    *value_ptr = skip_spaces(value);
 
  return 1;
}
Beispiel #2
0
void
rest_set_response_status(RESPONSE* response, status_code_t status)
{
#ifdef WITH_COAP
  coap_set_code(response, status);
#else /*WITH_COAP*/
  http_set_status(response, status);
#endif /*WITH_COAP*/
}
Beispiel #3
0
static int
http_handle_host_header(http_t *ctx, const char *name, char *value)
{
  char *ep;

  RUNTIME_ASSERT(ctx);
  RUNTIME_ASSERT(name);
  RUNTIME_ASSERT(value);
 
  if ('\0' != value[0]) {
    ep = http_parse_host(value);
    if (!ep) {
      goto bad_host;
    }
  } else {
    ep = value;
  }

  if (':' != *ep) {
    ctx->port = 0;
  } else {
    ep++;
    if (!parse_port_number(ep, &ctx->port, &ep)) {
      http_set_status(ctx, 400, "Bad Request (Bad Port)");
      return -1;
    }
  }

  ep = skip_spaces(ep);
  if ('\0' != *ep) {
    goto bad_host;
  }

  http_set_host(ctx, value);

  return 0;

bad_host:
  
  http_set_status(ctx, 400, "Bad Request (Bad Host)");
  return -1;
}
Beispiel #4
0
static int
http_handle_transfer_encoding_header(http_t *ctx,
    const char *name, char *value)
{
  RUNTIME_ASSERT(ctx);
  RUNTIME_ASSERT(name);
  RUNTIME_ASSERT(value);

  if (0 == strcasecmp(value, "chunked")) {
    ctx->encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
    return 0;
  }
  
  http_set_status(ctx, 400, "Bad Request (Bad Transfer-Encoding)");
  return -1;
}
Beispiel #5
0
/**
 *  @return
 *   -1 if a header was invalid (see http status for details)
 *    0 if all headers are successfully handled (end-of-header reached)
 */
int
http_process_headers(http_t *ctx, char *buf, size_t buf_size)
{
  RUNTIME_ASSERT(ctx);
  RUNTIME_ASSERT(HTTP_STATE_HEADERS == ctx->state);
  
  for (;;) {
    char *name, *value;
    
    name = value =  NULL;
    switch (http_parse_header(ctx, buf, buf_size, &name, &value)) {
    case -1:
      DBUG("http_parse_header() failed: %u \"%s\"",
        ctx->status_code, ctx->status_msg);
      return -1;
      
    case 0:
      RUNTIME_ASSERT(HTTP_STATE_BODY == ctx->state);
      return 0;

    case 1:
      break;

    default:
      RUNTIME_ASSERT(0);
      http_set_status(ctx, 500, "Internal Server Error");
      return -1;
    }

    RUNTIME_ASSERT(HTTP_STATE_HEADERS == ctx->state);
    RUNTIME_ASSERT(name);
    RUNTIME_ASSERT(value);

#if 0
    http_log(ctx, "name=\"%s\"; value=\"%s\"", name, value);
#endif
    
    if (0 != http_handle_header(ctx, name, value))
      break;
  }

  return -1;
}
Beispiel #6
0
static int
http_handle_content_length_header(http_t *ctx, const char *name, char *value)
{
  uint64_t u;
  int error;
  char *ep;

  RUNTIME_ASSERT(ctx);
  RUNTIME_ASSERT(name);
  RUNTIME_ASSERT(value);
  
  u = parse_uint64(value, &ep, 10, &error);
  if (error || '\0' != *ep) {
    http_set_status(ctx, 400, "Bad Request (Bad Content-Length)");
    return -1;
  }
  ctx->content_length = u;

  return 0;
}
Beispiel #7
0
/**
 * Reads a single HTTP header line from ctx->input and parses it.
 * Header continuation is handled transparently. Trailing space characters
 * are discarded from the header line and the line is NUL-terminated.
 * 
 * @param ctx an initialized ``http_t'' in state HTTP_STATE_HEADERS.
 * @param buf a work buffer.
 * @param buf_size the size of buf; this defines the maximum length of
 *        headers that can be handled.
 *
 * @return
 *   -1 if a header was invalid (see http status for details).
 *    0 if the end-of-headers was reached.
 *    1 if a header was successfully parsed.
 */
int
http_read_header(http_t *ctx, char *buf, size_t buf_size)
{
  char *p;
  size_t size;
 
  RUNTIME_ASSERT(ctx);
  RUNTIME_ASSERT(buf);
  RUNTIME_ASSERT(buf_size > 0);
  RUNTIME_ASSERT(ctx->state == HTTP_STATE_HEADERS);

  p = buf;
  size = buf_size;

  for (;;) {
    ssize_t r;
    
    r = fifo_findchar(ctx->input, '\n', size);
    if ((ssize_t) -1 == r) {
      if ((size_t) fifo_fill(ctx->input) > size)
        DBUG("Header line is too long");
      else
        DBUG("Header line is not yet terminated");

      ctx->keep_alive = false;
      http_set_status(ctx, 413, "Request Entity Too Large");
      return -1;
    }

    RUNTIME_ASSERT(r >= 0 && (size_t) r < size);

    /* Make sure the headers don't contain any NUL characters */
    if ((ssize_t) -1 != fifo_findchar(ctx->input, '\0', r)) {
      ctx->keep_alive = false;
      http_set_status(ctx, 400, "Bad Request (NUL in Header)");
      return -1;
    }

    fifo_read(ctx->input, p, 1 + r);
    RUNTIME_ASSERT('\n' == p[r]);

    /* Discard trailing '\r' characters */
    for (/* NOTHING*/; r > 0; r--) {
      if ('\r' != p[r - 1])
        break;
    }
    
    RUNTIME_ASSERT('\n' == p[r] || '\r' == p[r]);
    p[r] = '\0';

    /* Check for a header continuation with HT (0x09) or a space (0x20) */
    if (
      0 != fifo_findchar(ctx->input, 0x09, 1) &&
      0 != fifo_findchar(ctx->input, 0x20, 1)
    ) {
     
      /* Discard all trailing spaces */
      if (isspace((unsigned char) p[r])) {
        while (r > 0 && isspace((unsigned char) p[r - 1]))
          r--;

        p[r] = '\0';
      }
    
      break;
    }

    p += r;
    size -= r;

    /* Discard all consecutive HT and SP characters */
    fifo_skip_chars(ctx->input, fifo_fill(ctx->input), "\x09\x20");

    /* Replace all skipped space by a single space character */
    if (size > 0) {
      *p++ = 0x20;
      size--;
    }
  }
 
  if (ctx->debug_dump_headers) {
    http_log(ctx, "\t%s", buf);
  }

  if ('\0' == *buf) {
    ctx->state = HTTP_STATE_BODY;
    return 0;
  }
  
  return 1;
}
Beispiel #8
0
/* This function is just a compliant action wrapper for "set-status". */
static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
                                              struct session *sess, struct stream *s, int flags)
{
	http_set_status(rule->arg.status.code, rule->arg.status.reason, s);
	return ACT_RET_CONT;
}
Beispiel #9
0
static
PT_THREAD(handle_request(connection_state_t* conn_state))
{
    static int error;
    const char* content_len;

    PSOCK_BEGIN(&(conn_state->sin));

    content_len = NULL;

    error = HTTP_NO_ERROR; /*always reinit static variables due to protothreads*/

    PRINTF("Request--->\n");

    //read method
    PSOCK_READTO(&(conn_state->sin), ' ');

    if (!is_method_handled(conn_state, conn_state->inputbuf)) {
        /*method not handled*/
        http_set_status(&conn_state->response, SERVICE_UNAVAILABLE_503);
        conn_state->state = STATE_OUTPUT;
    } else {
        /*read until the end of url*/
        PSOCK_READTO(&(conn_state->sin), ' ');

        /*-1 is needed since it also includes space char*/
        if (conn_state->inputbuf[PSOCK_DATALEN(&(conn_state->sin)) - 1] != ' ' ) {
            error = HTTP_URL_TOO_LONG;
        }

        conn_state->inputbuf[PSOCK_DATALEN(&(conn_state->sin)) - 1] = 0;

        PRINTF("Read URL:%s\n", conn_state->inputbuf);

        if (error == HTTP_NO_ERROR) {
            error = parse_url(conn_state, conn_state->inputbuf);
        }

        if (error != HTTP_NO_ERROR) {
            if (error == HTTP_URL_TOO_LONG) {
                http_set_status(&conn_state->response, REQUEST_URI_TOO_LONG_414);
            } else {
                http_set_status(&conn_state->response, BAD_REQUEST_400);
            }

            conn_state->state = STATE_OUTPUT;
        } else {
            /*read until the end of HTTP version - not used yet*/
            PSOCK_READTO(&(conn_state->sin), LINE_FEED_CHAR);

            PRINTF("After URL:%s\n", conn_state->inputbuf);

            /*FIXME : PSOCK_READTO takes just a single delimiter so I read till the end of line
            but now it may not fit in the buffer. If PSOCK_READTO would take two delimiters,
            we would have read until : and <CR> so it would not be blocked.*/

            /*Read the headers and store the necessary ones*/
            do {
                /*read the next line*/
                PSOCK_READTO(&(conn_state->sin), LINE_FEED_CHAR);
                conn_state->inputbuf[ PSOCK_DATALEN(&(conn_state->sin)) - 1] = 0;

                /*if headers finished then stop the infinite loop*/
                if (conn_state->inputbuf[0] == CARRIAGE_RETURN_CHAR || conn_state->inputbuf[0] == 0) {
                    PRINTF("Finished Headers!\n\n");
                    break;
                }

                parse_header(conn_state, conn_state->inputbuf);
            }
            while(1);

            content_len = get_header(conn_state->request.headers, HTTP_HEADER_NAME_CONTENT_LENGTH);
            if (content_len) {
                conn_state->request.payload_len = atoi(content_len);

                PRINTF("Post Data Size string: %s int: %d\n", content_len, conn_state->request.payload_len);
            }

            if (conn_state->request.payload_len) {
                static uint16_t read_bytes = 0;
                /*init the static variable again*/
                read_bytes = 0;

                conn_state->request.payload = allocate_buffer(conn_state->request.payload_len + 1);

                if (conn_state->request.payload) {
                    do {
                        PSOCK_READBUF(&(conn_state->sin));
                        /*null terminate the buffer in case it is a string.*/
                        conn_state->inputbuf[PSOCK_DATALEN(&(conn_state->sin))] = 0;

                        memcpy(conn_state->request.payload + read_bytes, conn_state->inputbuf, PSOCK_DATALEN(&(conn_state->sin)));

                        read_bytes += PSOCK_DATALEN(&(conn_state->sin));

                    } while (read_bytes < conn_state->request.payload_len);

                    conn_state->request.payload[read_bytes++] = 0;

                    PRINTF("PostData => %s \n", conn_state->request.payload);
                } else {
                    error = HTTP_MEMORY_ALLOC_ERR;
                }
            }

            if (error == HTTP_NO_ERROR) {
                if (service_cbk) {
                    service_cbk(&conn_state->request, &conn_state->response);
                }
            } else {
                PRINTF("Error:%d\n",error);
                http_set_status(&conn_state->response, INTERNAL_SERVER_ERROR_500);
            }

            conn_state->state = STATE_OUTPUT;
        }
    }

    PSOCK_END(&(conn_state->sin));
}