📄 http_resp.c
字号:
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is MPEG4IP.
*
* The Initial Developer of the Original Code is Cisco Systems Inc.
* Portions created by Cisco Systems Inc. are
* Copyright (C) Cisco Systems Inc. 2001. All Rights Reserved.
*
* Contributor(s):
* Bill May wmay@cisco.com
*/
/*
* http_resp.c - read and decode http response.
*/
#include "systems.h"
#ifdef HAVE_POLL
#include <sys/poll.h>
#endif
#include "http_private.h"
/*
* http_recv - receive up to len bytes in the buffer
*/
static int http_recv (int server_socket,
char *buffer,
uint32_t len)
{
int ret;
#ifdef HAVE_POLL
struct pollfd pollit;
pollit.fd = server_socket;
pollit.events = POLLIN | POLLPRI;
pollit.revents = 0;
ret = poll(&pollit, 1, 2000);
#else
fd_set read_set;
struct timeval timeout;
FD_ZERO(&read_set);
FD_SET(server_socket, &read_set);
timeout.tv_sec = 2;
timeout.tv_usec = 0;
ret = select(server_socket + 1, &read_set, NULL, NULL, &timeout);
#endif
if (ret <= 0) {
return -1;
}
ret = recv(server_socket, buffer, len, 0);
http_debug(LOG_DEBUG, "Return from recv is %d", ret);
return ret;
}
/*
* http_read_into_buffer - read bytes into buffer at the buffer offset
* to the end of the buffer.
*/
static int http_read_into_buffer (http_client_t *cptr,
uint32_t buffer_offset)
{
int ret;
ret = http_recv(cptr->m_server_socket,
cptr->m_resp_buffer + buffer_offset,
RESP_BUF_SIZE - buffer_offset);
if (ret <= 0) return (ret);
cptr->m_buffer_len = buffer_offset + ret;
return ret;
}
/*
* http_get_next_line()
* Use the existing buffers that we've read, and try to get a coherent
* line. This saves having to do a bunch of 1 byte reads looking for a
* cr/lf - instead, we read as many bytes as we can into the buffer, and
* then process it.
*/
static const char *http_get_next_line (http_client_t *cptr)
{
int ret;
uint32_t ix;
int last_on;
/*
* If we don't have any data, try to read a buffer full
*/
if (cptr->m_buffer_len <= 0) {
cptr->m_offset_on = 0;
ret = http_read_into_buffer(cptr, 0);
if (ret <= 0) {
return (NULL);
}
}
/*
* Look for CR/LF in the buffer. If we find it, NULL terminate at
* the CR, then set the buffer values to look past the crlf
*/
for (ix = cptr->m_offset_on + 1;
ix < cptr->m_buffer_len;
ix++) {
if (cptr->m_resp_buffer[ix] == '\n' &&
cptr->m_resp_buffer[ix - 1] == '\r') {
const char *retval = &cptr->m_resp_buffer[cptr->m_offset_on];
cptr->m_offset_on = ix + 1;
cptr->m_resp_buffer[ix - 1] = '\0'; // make it easy
return (retval);
}
}
if (cptr->m_offset_on == 0) {
return (NULL);
}
/*
* We don't have a line. So, move the data down in the buffer, then
* read into the rest of the buffer
*/
cptr->m_buffer_len -= cptr->m_offset_on;
if (cptr->m_buffer_len != 0) {
memmove(cptr->m_resp_buffer,
&cptr->m_resp_buffer[cptr->m_offset_on],
cptr->m_buffer_len);
last_on = cptr->m_buffer_len;
} else {
last_on = 1;
}
cptr->m_offset_on = 0;
ret = http_read_into_buffer(cptr, cptr->m_buffer_len);
if (ret <= 0) {
return (NULL);
}
/*
* Continue searching through the buffer. If we get this far, we've
* received like 2K or 4K without a CRLF - most likely a bad response
*/
for (ix = last_on;
ix < cptr->m_buffer_len;
ix++) {
if (cptr->m_resp_buffer[ix] == '\n' &&
cptr->m_resp_buffer[ix - 1] == '\r') {
const char *retval = &cptr->m_resp_buffer[cptr->m_offset_on];
cptr->m_offset_on = ix + 1;
cptr->m_resp_buffer[ix - 1] = '\0'; // make it easy
return (retval);
}
}
return (NULL);
}
/****************************************************************************
* HTTP header decoding
****************************************************************************/
#define HTTP_CMD_DECODE_FUNC(a) static void a(const char *lptr, http_client_t *cptr)
/*
* Connection: header
*/
HTTP_CMD_DECODE_FUNC(http_cmd_connection)
{
// connection can be comma seperated list.
while (*lptr != '\0') {
if (strncasecmp(lptr, "close", strlen("close")) == 0) {
cptr->m_connection_close = 1;
return;
} else {
while (*lptr != '\0' && *lptr != ',') lptr++;
}
}
}
/*
* Content-length: header
*/
HTTP_CMD_DECODE_FUNC(http_cmd_content_length)
{
cptr->m_content_len = 0;
while (isdigit(*lptr)) {
cptr->m_content_len_received = 1;
cptr->m_content_len *= 10;
cptr->m_content_len += *lptr - '0';
lptr++;
}
}
/*
* Content-type: header
*/
HTTP_CMD_DECODE_FUNC(http_cmd_content_type)
{
FREE_CHECK(cptr->m_resp, content_type);
cptr->m_resp->content_type = strdup(lptr);
}
/*
* Location: header
*/
HTTP_CMD_DECODE_FUNC(http_cmd_location)
{
FREE_CHECK(cptr, m_redir_location);
cptr->m_redir_location = strdup(lptr);
}
/*
* Transfer-Encoding: header
*/
HTTP_CMD_DECODE_FUNC(http_cmd_transfer_encoding)
{
do {
if (strncasecmp(lptr, "chunked", strlen("chunked")) == 0) {
cptr->m_transfer_encoding_chunked = 1;
return;
}
while ((*lptr != '\0') && (*lptr != ';')) lptr++;
ADV_SPACE(lptr);
} while (*lptr != '\0');
}
static struct {
const char *val;
uint32_t val_length;
void (*parse_routine)(const char *lptr, http_client_t *cptr);
} header_types[] =
{
#define HEAD_TYPE(a, b) { a, sizeof(a), b }
HEAD_TYPE("connection", http_cmd_connection),
HEAD_TYPE("content-length", http_cmd_content_length),
HEAD_TYPE("content-type", http_cmd_content_type),
HEAD_TYPE("location", http_cmd_location),
HEAD_TYPE("transfer-encoding", http_cmd_transfer_encoding),
{NULL, 0, NULL },
};
static void http_decode_header (http_client_t *cptr, const char *lptr)
{
int ix;
const char *after;
ix = 0;
ADV_SPACE(lptr);
while (header_types[ix].val != NULL) {
if (strncasecmp(lptr,
header_types[ix].val,
header_types[ix].val_length - 1) == 0) {
after = lptr + header_types[ix].val_length - 1;
ADV_SPACE(after);
if (*after == ':') {
after++;
ADV_SPACE(after);
/*
* Call the correct parsing routine
*/
(header_types[ix].parse_routine)(after, cptr);
return;
}
}
ix++;
}
//rtsp_debug(LOG_DEBUG, "Not processing response header: %s", lptr);
}
static uint32_t to_hex (const char **hex_string)
{
const char *p = *hex_string;
uint32_t ret = 0;
while (isxdigit(*p)) {
ret *= 16;
if (isdigit(*p)) {
ret += *p - '0';
} else {
ret += tolower(*p) - 'a' + 10;
}
p++;
}
*hex_string = p;
return (ret);
}
/*
* http_get_response - get the response, process it, and fill in the response
* structure.
*/
int http_get_response (http_client_t *cptr,
http_resp_t **resp)
{
const char *p;
int resp_code;
int ix;
int done;
uint32_t len;
int ret;
/*
* Clear out old response header
*/
if (*resp != NULL) {
cptr->m_resp = *resp;
http_resp_clear(*resp);
} else {
cptr->m_resp = (http_resp_t *)malloc(sizeof(http_resp_t));
memset(cptr->m_resp, 0, sizeof(http_resp_t));
*resp = cptr->m_resp;
}
/*
* Reset all relevent variables
*/
FREE_CHECK(cptr, m_redir_location);
cptr->m_connection_close = 0;
cptr->m_content_len_received = 0;
cptr->m_offset_on = 0;
cptr->m_buffer_len = 0;
cptr->m_transfer_encoding_chunked = 0;
/*
* Get the first line and process it
*/
p = http_get_next_line(cptr);
if (p == NULL) {
return (-1);
}
/*
* http/version.version processing
*/
ADV_SPACE(p);
if (*p == '\0' || strncasecmp(p, "http/", strlen("http/")) != 0) {
return (-1);
}
p += strlen("http/");
ADV_SPACE(p);
while (*p != '\0' && isdigit(*p)) p++;
if (*p++ != '.') return (-1);
while (*p != '\0' && isdigit(*p)) p++;
if (*p++ == '\0') return (-1);
ADV_SPACE(p);
/*
* error code processing - 200 is gold
*/
resp_code = 0;
for (ix = 0; ix < 3; ix++) {
if (isdigit(*p)) {
resp_code *= 10;
resp_code += *p++ - '0';
} else {
return (-1);
}
}
(*resp)->ret_code = resp_code;
ADV_SPACE(p);
if (*p != '\0') {
(*resp)->resp_phrase = strdup(p);
}
/*
* Now begin processing the headers
*/
done = 0;
do {
p = http_get_next_line(cptr);
if (p == NULL) {
return (-1);
}
if (*p == '\0') {
done = 1;
} else {
http_debug(LOG_DEBUG, p);
// we have a header. See if we want to process it...
http_decode_header(cptr, p);
}
} while (done == 0);
// Okay - at this point - we have the headers done. Let's
// read the body
if (cptr->m_content_len_received != 0) {
/*
* We have content-length - read that many bytes.
*/
if (cptr->m_content_len != 0) {
cptr->m_resp->body = (char *)malloc(cptr->m_content_len + 1);
len = cptr->m_buffer_len - cptr->m_offset_on;
memcpy(cptr->m_resp->body,
&cptr->m_resp_buffer[cptr->m_offset_on],
MIN(len, cptr->m_content_len));
while (len < cptr->m_content_len) {
ret = http_read_into_buffer(cptr, 0);
if (ret <= 0) {
return (-1);
}
memcpy(cptr->m_resp->body + len,
cptr->m_resp_buffer,
MIN(cptr->m_content_len - len, cptr->m_buffer_len));
len += cptr->m_buffer_len;
}
cptr->m_resp->body[cptr->m_content_len] = '\0';
cptr->m_resp->body_len = cptr->m_content_len;
}
} else if (cptr->m_transfer_encoding_chunked != 0) {
/*
* Chunk encoded - size in hex, body, size in hex, body, 0
*/
// read a line,
uint32_t te_size;
p = http_get_next_line(cptr);
if (p == NULL) {
http_debug(LOG_ALERT, "no chunk size reading chunk transitions");
return (-1);
}
te_size = to_hex(&p);
cptr->m_resp->body = NULL;
cptr->m_resp->body_len = 0;
/*
* Read a te_size chunk of bytes, read CRLF at end of that many bytes,
* read next size
*/
while (te_size != 0) {
http_debug(LOG_DEBUG, "Chunk size %d", te_size);
cptr->m_resp->body = (char *)realloc(cptr->m_resp->body,
cptr->m_resp->body_len + te_size);
len = MIN(te_size, cptr->m_buffer_len - cptr->m_offset_on);
memcpy(cptr->m_resp->body + cptr->m_resp->body_len,
&cptr->m_resp_buffer[cptr->m_offset_on],
len);
cptr->m_offset_on += len;
cptr->m_resp->body_len += len;
http_debug(LOG_DEBUG, "chunk - copied %d from rest of buffer(%d)",
len, cptr->m_resp->body_len);
while (len < te_size) {
int ret;
ret = http_recv(cptr->m_server_socket,
cptr->m_resp->body + cptr->m_resp->body_len,
te_size - len);
if (ret <= 0) return (-1);
len += ret;
cptr->m_resp->body_len += ret;
http_debug(LOG_DEBUG, "chunk - recved %d bytes (%d)",
ret, cptr->m_resp->body_len);
}
p = http_get_next_line(cptr); // should read CRLF at end
if (p == NULL || *p != '\0') {
http_debug(LOG_ALERT, "Http chunk reader - should be CRLF at end of chunk, is %s", p);
return (-1);
}
p = http_get_next_line(cptr); // read next size
if (p == NULL) {
http_debug(LOG_ALERT,"No chunk size after first");
return (-1);
}
te_size = to_hex(&p);
}
} else {
// No termination - just keep reading, I guess...
len = cptr->m_buffer_len - cptr->m_offset_on;
cptr->m_resp->body = (char *)malloc(len + 1);
cptr->m_resp->body_len = len;
memcpy(cptr->m_resp->body,
&cptr->m_resp_buffer[cptr->m_offset_on],
len);
http_debug(LOG_INFO, "Len bytes copied - %d", len);
while (http_read_into_buffer(cptr, 0) > 0) {
char *temp;
len = cptr->m_resp->body_len + cptr->m_buffer_len;
temp = realloc(cptr->m_resp->body, len + 1);
if (temp == NULL) {
return -1;
}
cptr->m_resp->body = temp;
memcpy(&cptr->m_resp->body[cptr->m_resp->body_len],
cptr->m_resp_buffer,
cptr->m_buffer_len);
cptr->m_resp->body_len = len;
http_debug(LOG_INFO, "Len bytes added - %d", len);
}
cptr->m_resp->body[cptr->m_resp->body_len] = '\0';
}
return (0);
}
/*
* http_resp_clear - clean out http_resp_t structure
*/
void http_resp_clear (http_resp_t *rptr)
{
FREE_CHECK(rptr, body);
FREE_CHECK(rptr, content_type);
FREE_CHECK(rptr, resp_phrase);
}
/*
* http_resp_free - clean out http_resp_t and free
*/
void http_resp_free (http_resp_t *rptr)
{
if (rptr != NULL) {
http_resp_clear(rptr);
free(rptr);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -