/** * @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; }
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*/ }
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; }
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; }
/** * @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; }
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; }
/** * 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; }
/* 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; }
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)); }