Files
2019-12-18 05:20:05 -08:00

502 lines
22 KiB
C

// Copyright (c) 2015-2019 The HomeKit ADK Contributors
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// See [CONTRIBUTORS.md] for the list of HomeKit ADK project authors.
#include "util_http_reader.h"
#define CR 13
#define LF 10
#define SP 32
#define HT 9
#define SUBSTATE_NONE 0
#define SUBSTATE_READING 1
#define SUBSTATE_AFTER_CR 2
#define SUBSTATE_AFTER_LF 3
#define SUBSTATE_AFTER_SP 4
void util_http_reader_init(struct util_http_reader* r, int type) {
HAPPrecondition(r != NULL);
if (type == util_HTTP_READER_TYPE_REQUEST) {
r->type = util_HTTP_READER_TYPE_REQUEST;
r->state = util_HTTP_READER_STATE_EXPECTING_METHOD;
} else {
HAPPrecondition(type == util_HTTP_READER_TYPE_RESPONSE);
r->type = util_HTTP_READER_TYPE_RESPONSE;
r->state = util_HTTP_READER_STATE_EXPECTING_VERSION;
}
r->substate = SUBSTATE_NONE;
r->in_quoted_pair = 0;
r->in_quoted_string = 0;
r->result_token = NULL;
r->result_length = 0;
}
HAP_RESULT_USE_CHECK
static int is_digit(int c) {
return ('0' <= c) && (c <= '9');
}
HAP_RESULT_USE_CHECK
static int is_whitespace(int c) {
return (c == SP) || (c == HT);
}
HAP_RESULT_USE_CHECK
static int is_token_char(int c) {
return (33 <= c) && (c < 127) && (c != '(') && (c != ')') && (c != '<') && (c != '>') && (c != '@') && (c != ',') &&
(c != ';') && (c != ':') && (c != '\\') && (c != '"') && (c != '/') && (c != '[') && (c != ']') &&
(c != '?') && (c != '=') && (c != '{') && (c != '}');
}
HAP_RESULT_USE_CHECK
static int is_uri_char(int c) {
return (('A' <= c) && (c <= 'Z')) || (('a' <= c) && (c <= 'z')) || (('0' <= c) && (c <= '9')) || (c == '%') ||
(c == '-') || (c == '.') || (c == '_') || (c == '~') || (c == ':') || (c == '/') || (c == '?') ||
(c == '#') || (c == '[') || (c == ']') || (c == '@') || (c == '!') || (c == '$') || (c == '&') ||
(c == '\'') || (c == '(') || (c == ')') || (c == '*') || (c == '+') || (c == ',') || (c == ';') ||
(c == '=');
}
HAP_RESULT_USE_CHECK
static int is_version_char(int c) {
return (c == 'H') || (c == 'T') || (c == 'P') || (c == '/') || (c == '.') || (('0' <= c) && (c <= '9'));
}
HAP_RESULT_USE_CHECK
static int is_text_char(int c) {
return (c < 0) || ((32 <= c) && (c < 127)) || (128 <= c) || (c == HT);
}
HAP_RESULT_USE_CHECK
static size_t skip_whitespace(char* buffer, size_t length) {
size_t n;
HAPPrecondition(buffer != NULL);
n = 0;
HAPAssert(n <= length);
while ((n < length) && is_whitespace(buffer[n])) {
n++;
}
HAPAssert((n == length) || ((n < length) && !is_whitespace(buffer[n])));
return n;
}
HAP_RESULT_USE_CHECK
static size_t read_octets(struct util_http_reader* r, char* buffer, size_t length, int (*predicate)(int)) {
size_t n;
HAPPrecondition(r != NULL);
HAPPrecondition(buffer != NULL);
HAPPrecondition(predicate != NULL);
n = 0;
HAPAssert(n <= length);
while ((n < length) && predicate(buffer[n])) {
n++;
}
HAPAssert((n == length) || ((n < length) && !predicate(buffer[n])));
r->result_token = buffer;
r->result_length = n;
return n;
}
HAP_RESULT_USE_CHECK
static size_t read_octets_and_quotes(struct util_http_reader* r, char* buffer, size_t length, int (*predicate)(int)) {
size_t n;
HAPPrecondition(r != NULL);
HAPPrecondition(buffer != NULL);
HAPPrecondition(predicate != NULL);
n = 0;
HAPAssert(n <= length);
while ((n < length) && (r->in_quoted_pair || predicate(buffer[n]))) {
if (r->in_quoted_pair) {
r->in_quoted_pair = 0;
} else if (r->in_quoted_string) {
if (buffer[n] == '\\') {
r->in_quoted_pair = 1;
} else if (buffer[n] == '"') {
r->in_quoted_string = 0;
}
} else if (buffer[n] == '"') {
r->in_quoted_string = 1;
}
n++;
}
HAPAssert((n == length) || ((n < length) && !r->in_quoted_pair && !predicate(buffer[n])));
r->result_token = buffer;
r->result_length = n;
return n;
}
HAP_RESULT_USE_CHECK
size_t util_http_reader_read(struct util_http_reader* r, char* buffer, size_t length) {
size_t n;
int post;
HAPPrecondition(r != NULL);
HAPPrecondition(buffer != NULL);
r->result_token = NULL;
r->result_length = 0;
n = 0;
HAPAssert(n <= length);
if (n < length) {
do {
switch (r->state) {
case util_HTTP_READER_STATE_EXPECTING_METHOD:
if (r->substate == SUBSTATE_NONE) {
n += skip_whitespace(&buffer[n], length - n);
HAPAssert(n <= length);
if (n < length) {
if (buffer[n] == CR) {
n++;
r->substate = SUBSTATE_AFTER_CR;
} else if (buffer[n] == LF) {
n++;
} else {
r->state = util_HTTP_READER_STATE_READING_METHOD;
}
}
} else {
HAPAssert(r->substate == SUBSTATE_AFTER_CR);
if (buffer[n] == LF) {
n++;
r->substate = SUBSTATE_NONE;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
}
break;
case util_HTTP_READER_STATE_READING_METHOD:
if (r->substate == SUBSTATE_NONE) {
if (is_token_char(buffer[n])) {
r->substate = SUBSTATE_READING;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_READING);
n += read_octets(r, &buffer[n], length - n, is_token_char);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_COMPLETED_METHOD;
r->substate = SUBSTATE_NONE;
}
}
break;
case util_HTTP_READER_STATE_COMPLETED_METHOD:
r->state = util_HTTP_READER_STATE_EXPECTING_URI;
break;
case util_HTTP_READER_STATE_EXPECTING_URI:
if (r->substate == SUBSTATE_NONE) {
if (is_whitespace(buffer[n])) {
n++;
r->substate = SUBSTATE_AFTER_SP;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_AFTER_SP);
n += skip_whitespace(&buffer[n], length - n);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_READING_URI;
r->substate = SUBSTATE_NONE;
}
}
break;
case util_HTTP_READER_STATE_READING_URI:
if (r->substate == SUBSTATE_NONE) {
if (is_uri_char(buffer[n])) {
r->substate = SUBSTATE_READING;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_READING);
n += read_octets(r, &buffer[n], length - n, is_uri_char);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_COMPLETED_URI;
r->substate = SUBSTATE_NONE;
}
}
break;
case util_HTTP_READER_STATE_COMPLETED_URI:
r->state = util_HTTP_READER_STATE_EXPECTING_VERSION;
break;
case util_HTTP_READER_STATE_EXPECTING_VERSION:
if (r->type == util_HTTP_READER_TYPE_REQUEST) {
if (r->substate == SUBSTATE_NONE) {
if (is_whitespace(buffer[n])) {
n++;
r->substate = SUBSTATE_AFTER_SP;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_AFTER_SP);
n += skip_whitespace(&buffer[n], length - n);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_READING_VERSION;
r->substate = SUBSTATE_NONE;
}
}
} else {
HAPAssert(r->type == util_HTTP_READER_TYPE_RESPONSE);
if (r->substate == SUBSTATE_NONE) {
n += skip_whitespace(&buffer[n], length - n);
HAPAssert(n <= length);
if (n < length) {
if (buffer[n] == CR) {
n++;
r->substate = SUBSTATE_AFTER_CR;
} else if (buffer[n] == LF) {
n++;
} else {
r->state = util_HTTP_READER_STATE_READING_VERSION;
}
}
} else {
HAPAssert(r->substate == SUBSTATE_AFTER_CR);
if (buffer[n] == LF) {
n++;
r->substate = SUBSTATE_NONE;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
}
}
break;
case util_HTTP_READER_STATE_READING_VERSION:
if (r->substate == SUBSTATE_NONE) {
if (is_version_char(buffer[n])) {
r->substate = SUBSTATE_READING;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_READING);
n += read_octets(r, &buffer[n], length - n, is_version_char);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_COMPLETED_VERSION;
r->substate = SUBSTATE_NONE;
}
}
break;
case util_HTTP_READER_STATE_COMPLETED_VERSION:
if (r->type == util_HTTP_READER_TYPE_REQUEST) {
r->state = util_HTTP_READER_STATE_EXPECTING_HEADER_NAME;
} else {
HAPAssert(r->type == util_HTTP_READER_TYPE_RESPONSE);
r->state = util_HTTP_READER_STATE_EXPECTING_STATUS;
}
break;
case util_HTTP_READER_STATE_EXPECTING_STATUS:
if (r->substate == SUBSTATE_NONE) {
if (is_whitespace(buffer[n])) {
n++;
r->substate = SUBSTATE_AFTER_SP;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_AFTER_SP);
n += skip_whitespace(&buffer[n], length - n);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_READING_STATUS;
r->substate = SUBSTATE_NONE;
}
}
break;
case util_HTTP_READER_STATE_READING_STATUS:
if (r->substate == SUBSTATE_NONE) {
if (is_digit(buffer[n])) {
r->substate = SUBSTATE_READING;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_READING);
n += read_octets(r, &buffer[n], length - n, is_digit);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_COMPLETED_STATUS;
r->substate = SUBSTATE_NONE;
}
}
break;
case util_HTTP_READER_STATE_COMPLETED_STATUS:
r->state = util_HTTP_READER_STATE_EXPECTING_REASON;
break;
case util_HTTP_READER_STATE_EXPECTING_REASON:
if (r->substate == SUBSTATE_NONE) {
if (is_whitespace(buffer[n])) {
n++;
r->substate = SUBSTATE_AFTER_SP;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_AFTER_SP);
n += skip_whitespace(&buffer[n], length - n);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_READING_REASON;
r->substate = SUBSTATE_NONE;
}
}
break;
case util_HTTP_READER_STATE_READING_REASON:
n += read_octets(r, &buffer[n], length - n, is_text_char);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_COMPLETED_REASON;
}
break;
case util_HTTP_READER_STATE_COMPLETED_REASON:
r->state = util_HTTP_READER_STATE_EXPECTING_HEADER_NAME;
break;
case util_HTTP_READER_STATE_EXPECTING_HEADER_NAME:
if (r->substate == SUBSTATE_NONE) {
if (buffer[n] == CR) {
n++;
r->substate = SUBSTATE_AFTER_CR;
} else if (buffer[n] == LF) {
n++;
r->state = util_HTTP_READER_STATE_READING_HEADER_NAME;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_AFTER_CR);
if (buffer[n] == LF) {
n++;
r->state = util_HTTP_READER_STATE_READING_HEADER_NAME;
r->substate = SUBSTATE_NONE;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
}
break;
case util_HTTP_READER_STATE_READING_HEADER_NAME:
if (r->substate == SUBSTATE_NONE) {
if (is_token_char(buffer[n])) {
r->substate = SUBSTATE_READING;
} else {
r->state = util_HTTP_READER_STATE_ENDING_HEADER_LINES;
}
} else {
HAPAssert(r->substate == SUBSTATE_READING);
n += read_octets(r, &buffer[n], length - n, is_token_char);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_COMPLETED_HEADER_NAME;
r->substate = SUBSTATE_NONE;
}
}
break;
case util_HTTP_READER_STATE_COMPLETED_HEADER_NAME:
r->state = util_HTTP_READER_STATE_EXPECTING_HEADER_VALUE;
break;
case util_HTTP_READER_STATE_EXPECTING_HEADER_VALUE:
if (buffer[n] == ':') {
n++;
r->state = util_HTTP_READER_STATE_READING_HEADER_VALUE;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
break;
case util_HTTP_READER_STATE_READING_HEADER_VALUE:
n += read_octets_and_quotes(r, &buffer[n], length - n, is_text_char);
HAPAssert(n <= length);
if (n < length) {
r->state = util_HTTP_READER_STATE_COMPLETED_HEADER_VALUE;
}
break;
case util_HTTP_READER_STATE_COMPLETED_HEADER_VALUE:
r->state = util_HTTP_READER_STATE_ENDING_HEADER_LINE;
break;
case util_HTTP_READER_STATE_ENDING_HEADER_LINE:
if (r->substate == SUBSTATE_NONE) {
if (buffer[n] == CR) {
n++;
r->substate = SUBSTATE_AFTER_CR;
} else if (buffer[n] == LF) {
n++;
r->substate = SUBSTATE_AFTER_LF;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else if (r->substate == SUBSTATE_AFTER_CR) {
if (buffer[n] == LF) {
n++;
r->substate = SUBSTATE_AFTER_LF;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_AFTER_LF);
if (is_whitespace(buffer[n])) {
r->state = util_HTTP_READER_STATE_READING_HEADER_VALUE;
r->substate = SUBSTATE_NONE;
} else if (r->in_quoted_string) {
r->state = util_HTTP_READER_STATE_ERROR;
} else {
r->state = util_HTTP_READER_STATE_READING_HEADER_NAME;
r->substate = SUBSTATE_NONE;
}
}
break;
case util_HTTP_READER_STATE_ENDING_HEADER_LINES:
if (r->substate == SUBSTATE_NONE) {
if (buffer[n] == CR) {
n++;
r->substate = SUBSTATE_AFTER_CR;
} else if (buffer[n] == LF) {
n++;
r->state = util_HTTP_READER_STATE_DONE;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
} else {
HAPAssert(r->substate == SUBSTATE_AFTER_CR);
if (buffer[n] == LF) {
n++;
r->state = util_HTTP_READER_STATE_DONE;
r->substate = SUBSTATE_NONE;
} else {
r->state = util_HTTP_READER_STATE_ERROR;
}
}
break;
case util_HTTP_READER_STATE_DONE:
case util_HTTP_READER_STATE_ERROR:
break;
default:
HAPFatalError();
break;
}
} while ((n < length) && (r->state != util_HTTP_READER_STATE_COMPLETED_METHOD) &&
(r->state != util_HTTP_READER_STATE_COMPLETED_URI) &&
(r->state != util_HTTP_READER_STATE_COMPLETED_VERSION) &&
(r->state != util_HTTP_READER_STATE_COMPLETED_STATUS) &&
(r->state != util_HTTP_READER_STATE_COMPLETED_REASON) &&
(r->state != util_HTTP_READER_STATE_COMPLETED_HEADER_NAME) &&
(r->state != util_HTTP_READER_STATE_COMPLETED_HEADER_VALUE) &&
(r->state != util_HTTP_READER_STATE_DONE) && (r->state != util_HTTP_READER_STATE_ERROR));
}
post = (n == length) ||
((n < length) &&
((r->state == util_HTTP_READER_STATE_ERROR) || (r->state == util_HTTP_READER_STATE_COMPLETED_METHOD) ||
(r->state == util_HTTP_READER_STATE_COMPLETED_URI) ||
(r->state == util_HTTP_READER_STATE_COMPLETED_VERSION) ||
(r->state == util_HTTP_READER_STATE_COMPLETED_STATUS) ||
(r->state == util_HTTP_READER_STATE_COMPLETED_REASON) ||
(r->state == util_HTTP_READER_STATE_COMPLETED_HEADER_NAME) ||
(r->state == util_HTTP_READER_STATE_COMPLETED_HEADER_VALUE) || (r->state == util_HTTP_READER_STATE_DONE)));
HAPAssert(post);
return n;
}