in sdk/src/azure/core/az_http_response.c [139:280]
AZ_NODISCARD az_result az_http_response_get_next_header(
az_http_response* ref_response,
az_span* out_name,
az_span* out_value)
{
_az_PRECONDITION_NOT_NULL(ref_response);
_az_PRECONDITION_NOT_NULL(out_name);
_az_PRECONDITION_NOT_NULL(out_value);
az_span* reader = &ref_response->_internal.parser.remaining;
{
_az_http_response_kind const kind = ref_response->_internal.parser.next_kind;
// if reader is expecting to read body (all headers were read), return
// AZ_ERROR_HTTP_END_OF_HEADERS so we know we reach end of headers
if (kind == _az_HTTP_RESPONSE_KIND_BODY)
{
return AZ_ERROR_HTTP_END_OF_HEADERS;
}
// Can't read a header if status line was not previously called,
// User needs to call az_http_response_status_line() which would reset parser and set kind to
// headers
if (kind != _az_HTTP_RESPONSE_KIND_HEADER)
{
return AZ_ERROR_HTTP_INVALID_STATE;
}
}
if (az_span_size(ref_response->_internal.parser.remaining) == 0)
{
// avoid reading address if span is size 0
return AZ_ERROR_HTTP_CORRUPT_RESPONSE_HEADER;
}
// check if we are at the end of all headers to change state to Body.
// We keep state to Headers if current char is not '\r' (there is another header)
if (az_span_ptr(ref_response->_internal.parser.remaining)[0] == '\r')
{
_az_RETURN_IF_FAILED(_az_is_expected_span(reader, AZ_SPAN_FROM_STR("\r\n")));
ref_response->_internal.parser.next_kind = _az_HTTP_RESPONSE_KIND_BODY;
return AZ_ERROR_HTTP_END_OF_HEADERS;
}
// https://tools.ietf.org/html/rfc7230#section-3.2
// header-field = field-name ":" OWS field-value OWS
// field-name = token
{
// https://tools.ietf.org/html/rfc7230#section-3.2.6
// token = 1*tchar
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" /
// "_" / "`" / "|" / "~" / DIGIT / ALPHA;
// any VCHAR,
// except delimiters
int32_t field_name_length = 0;
int32_t input_size = az_span_size(*reader);
uint8_t const* const ptr = az_span_ptr(*reader);
for (; field_name_length < input_size; ++field_name_length)
{
uint8_t next_byte = ptr[field_name_length];
if (next_byte == ':')
{
break;
}
if (!az_http_valid_token[next_byte])
{
return AZ_ERROR_HTTP_CORRUPT_RESPONSE_HEADER;
}
}
if (field_name_length == input_size)
{
return AZ_ERROR_HTTP_CORRUPT_RESPONSE_HEADER;
}
// form a header name. Reader is currently at char ':'
*out_name = az_span_slice(*reader, 0, field_name_length);
// update reader to next position after colon (add one)
*reader = az_span_slice_to_end(*reader, field_name_length + 1);
// Remove whitespace characters from header name
// https://github.com/Azure/azure-sdk-for-c/issues/604
*out_name = _az_span_trim_whitespace(*out_name);
// OWS -> remove the optional whitespace characters before header value
int32_t ows_len = 0;
input_size = az_span_size(*reader);
uint8_t const* const ptr_space = az_span_ptr(*reader);
for (; ows_len < input_size; ++ows_len)
{
uint8_t next_byte = ptr_space[ows_len];
if (next_byte != ' ' && next_byte != '\t')
{
break;
}
}
if (ows_len == input_size)
{
return AZ_ERROR_HTTP_CORRUPT_RESPONSE_HEADER;
}
*reader = az_span_slice_to_end(*reader, ows_len);
}
// field-value = *( field-content / obs-fold )
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
// field-vchar = VCHAR / obs-text
//
// obs-fold = CRLF 1*( SP / HTAB )
// ; obsolete line folding
// ; see Section 3.2.4
//
// Note: obs-fold is not implemented.
{
int32_t offset = 0;
int32_t offset_value_end = offset;
while (true)
{
uint8_t c = az_span_ptr(*reader)[offset];
offset += 1;
if (c == '\r')
{
break; // break as soon as end of value char is found
}
if (_az_is_http_whitespace(c))
{
continue; // whitespace or tab is accepted. It can be any number after value (OWS)
}
if (c < ' ')
{
return AZ_ERROR_HTTP_CORRUPT_RESPONSE_HEADER;
}
offset_value_end = offset; // increasing index only for valid chars,
}
*out_value = az_span_slice(*reader, 0, offset_value_end);
// moving reader. It is currently after \r was found
*reader = az_span_slice_to_end(*reader, offset);
// Remove whitespace characters from value https://github.com/Azure/azure-sdk-for-c/issues/604
*out_value = _az_span_trim_whitespace_from_end(*out_value);
}
_az_RETURN_IF_FAILED(_az_is_expected_span(reader, AZ_SPAN_FROM_STR("\n")));
return AZ_OK;
}