Commit 4a10d7e7 authored by Philipp Schafft's avatar Philipp Schafft 🦁

Merge branch 'ph3-body'

parents 2d3409af a5327006
......@@ -53,7 +53,7 @@
<!-- Form to add Users -->
<xsl:if test="@can-adduser = 'true'">
<h4>Add User</h4>
<form method="get" action="manageauth.xsl">
<form method="post" action="manageauth.xsl">
<label for="username" class="hidden">Username</label>
<input type="text" id="username" name="username" value="" placeholder="Username" required="required" />
<label for="password" class="hidden">Password</label>
......
......@@ -18,7 +18,7 @@
<xsl:choose>
<xsl:when test="source">
<p>Choose the mountpoint to which you want to move the listeners to:</p>
<form method="get" action="moveclients.xsl">
<form method="post" action="moveclients.xsl">
<label for="moveto" class="hidden">
Move from <code><xsl:value-of select="current_source" /></code> to
</label>
......
......@@ -15,7 +15,7 @@
<h3>Mountpoint <xsl:value-of select="@mount" /></h3>
<!-- Mount nav -->
<xsl:call-template name="mountnav" />
<form method="get" action="/admin/metadata.xsl">
<form method="post" action="/admin/metadata.xsl">
<label for="metadata" class="hidden">Metadata</label>
<input type="text" id="metadata" name="song" value="" placeholder="Click to edit" required="required" />
<input type="hidden" name="mount" value="{@mount}" />
......
......@@ -52,7 +52,7 @@
/* Helper macros */
#define COMMAND_REQUIRE(client,name,var) \
do { \
(var) = httpp_get_query_param((client)->parser, (name)); \
(var) = httpp_get_param((client)->parser, (name)); \
if((var) == NULL) { \
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_MISSING_PARAMETER); \
return; \
......@@ -60,7 +60,7 @@
} while(0);
#define COMMAND_OPTIONAL(client,name,var) \
(var) = httpp_get_query_param((client)->parser, (name))
(var) = httpp_get_param((client)->parser, (name))
/* special commands */
#define COMMAND_ERROR ADMIN_COMMAND_ERROR
......@@ -502,7 +502,7 @@ void admin_handle_request(client_t *client, const char *uri)
}
}
mount = httpp_get_query_param(client->parser, "mount");
COMMAND_OPTIONAL(client, "mount", mount);
/* Find mountpoint source */
if(mount != NULL) {
......@@ -543,6 +543,7 @@ void admin_handle_request(client_t *client, const char *uri)
switch (client->parser->req_type) {
case httpp_req_get:
case httpp_req_post:
handler->function(client, source, format);
break;
case httpp_req_options:
......
......@@ -49,11 +49,13 @@
#define CONFIG_DEFAULT_CLIENT_LIMIT 256
#define CONFIG_DEFAULT_SOURCE_LIMIT 16
#define CONFIG_DEFAULT_QUEUE_SIZE_LIMIT (500*1024)
#define CONFIG_DEFAULT_BODY_SIZE_LIMIT (4*1024)
#define CONFIG_DEFAULT_BURST_SIZE (64*1024)
#define CONFIG_DEFAULT_THREADPOOL_SIZE 4
#define CONFIG_DEFAULT_CLIENT_TIMEOUT 30
#define CONFIG_DEFAULT_HEADER_TIMEOUT 15
#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
#define CONFIG_DEFAULT_BODY_TIMEOUT (10 + CONFIG_DEFAULT_HEADER_TIMEOUT)
#define CONFIG_DEFAULT_MASTER_USERNAME "relay"
#define CONFIG_DEFAULT_SHOUTCAST_MOUNT "/stream"
#define CONFIG_DEFAULT_SHOUTCAST_USER "source"
......@@ -801,12 +803,16 @@ static void _set_defaults(ice_config_t *configuration)
->source_limit = CONFIG_DEFAULT_SOURCE_LIMIT;
configuration
->queue_size_limit = CONFIG_DEFAULT_QUEUE_SIZE_LIMIT;
configuration
->body_size_limit = CONFIG_DEFAULT_BODY_SIZE_LIMIT;
configuration
->client_timeout = CONFIG_DEFAULT_CLIENT_TIMEOUT;
configuration
->header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
configuration
->source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
configuration
->source_timeout = CONFIG_DEFAULT_BODY_TIMEOUT;
configuration
->shoutcast_mount = (char *) xmlCharStrdup(CONFIG_DEFAULT_SHOUTCAST_MOUNT);
configuration
......@@ -1123,6 +1129,8 @@ static void _parse_limits(xmlDocPtr doc,
__read_int(doc, node, &configuration->client_limit, "<clients> must not be empty.");
} else if (xmlStrcmp(node->name, XMLSTR("sources")) == 0) {
__read_int(doc, node, &configuration->source_limit, "<sources> must not be empty.");
} else if (xmlStrcmp(node->name, XMLSTR("bodysize")) == 0) {
__read_int(doc, node, &configuration->body_size_limit, "<bodysize> must not be empty.");
} else if (xmlStrcmp(node->name, XMLSTR("queue-size")) == 0) {
__read_unsigned_int(doc, node, &configuration->queue_size_limit, "<queue-size> must not be empty.");
} else if (xmlStrcmp(node->name, XMLSTR("threadpool")) == 0) {
......@@ -1134,6 +1142,8 @@ static void _parse_limits(xmlDocPtr doc,
__read_int(doc, node, &configuration->header_timeout, "<header-timeout> must not be empty.");
} else if (xmlStrcmp(node->name, XMLSTR("source-timeout")) == 0) {
__read_int(doc, node, &configuration->source_timeout, "<source-timeout> must not be empty.");
} else if (xmlStrcmp(node->name, XMLSTR("body-timeout")) == 0) {
__read_int(doc, node, &configuration->body_timeout, "<body-timeout> must not be empty.");
} else if (xmlStrcmp(node->name, XMLSTR("burst-on-connect")) == 0) {
ICECAST_LOG_WARN("<burst-on-connect> is deprecated, use <burst-size> instead.");
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
......
......@@ -26,6 +26,7 @@
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "icecasttypes.h"
#include "compat.h"
#define XMLSTR(str) ((xmlChar *)(str))
......@@ -170,11 +171,13 @@ struct ice_config_tag {
int client_limit;
int source_limit;
int body_size_limit;
unsigned int queue_size_limit;
unsigned int burst_size;
int client_timeout;
int header_timeout;
int source_timeout;
int body_timeout;
int fileserve;
int on_demand; /* global setting for all relays */
......
......@@ -32,6 +32,7 @@
#include "refobject.h"
#include "cfgfile.h"
#include "connection.h"
#include "tls.h"
#include "refbuf.h"
#include "format.h"
#include "stats.h"
......@@ -86,6 +87,8 @@ int client_create(client_t **c_ptr, connection_t *con, http_parser_t *parser)
client->con = con;
client->parser = parser;
client->protocol = ICECAST_PROTOCOL_HTTP;
client->request_body_length = 0;
client->request_body_read = 0;
client->admin_command = ADMIN_COMMAND_ERROR;
client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
client->refbuf->len = 0; /* force reader code to ignore buffer contents */
......@@ -121,6 +124,16 @@ static inline void client_reuseconnection(client_t *client) {
client->con->send = NULL;
}
if (client->con->readbufferlen) {
/* Aend... moorre paaiin.
* stealing putback buffer.
*/
con->readbuffer = client->con->readbuffer;
con->readbufferlen = client->con->readbufferlen;
client->con->readbuffer = NULL;
client->con->readbufferlen = 0;
}
client->reuse = ICECAST_REUSE_CLOSE;
client_destroy(client);
......@@ -137,8 +150,11 @@ void client_destroy(client_t *client)
return;
if (client->reuse != ICECAST_REUSE_CLOSE) {
client_reuseconnection(client);
return;
/* only reuse the client if we reached the body's EOF. */
if (client_body_eof(client) == 1) {
client_reuseconnection(client);
return;
}
}
/* release the buffer now, as the buffer could be on the source queue
......@@ -435,3 +451,176 @@ void client_set_queue(client_t *client, refbuf_t *refbuf)
if (to_release)
refbuf_release(to_release);
}
ssize_t client_body_read(client_t *client, void *buf, size_t len)
{
ssize_t ret;
ICECAST_LOG_DEBUG("Reading from body (client=%p)", client);
if (client->request_body_length != -1) {
size_t left = (size_t)client->request_body_length - client->request_body_read;
if (len > left) {
ICECAST_LOG_DEBUG("Limiting read request to left over body size: left %zu byte, requested %zu byte", left, len);
len = left;
}
}
ret = client_read_bytes(client, buf, len);
if (ret > 0) {
client->request_body_read += ret;
}
return ret;
}
/* we might un-static this if needed at some time in distant future. -- ph3-der-loewe, 2018-04-17 */
static int client_eof(client_t *client)
{
if (!client)
return -1;
if (!client->con)
return 0;
if (client->con->tls && tls_got_shutdown(client->con->tls) > 1)
client->con->error = 1;
if (client->con->error)
return 1;
return 0;
}
int client_body_eof(client_t *client)
{
int ret = -1;
if (!client)
return -1;
if (client->request_body_length != -1 && client->request_body_read == (size_t)client->request_body_length) {
ICECAST_LOG_DEBUG("Reached given body length (client=%p)", client);
ret = 1;
} else if (client->encoding) {
ICECAST_LOG_DEBUG("Looking for body EOF with encoding (client=%p)", client);
ret = httpp_encoding_eof(client->encoding, (int(*)(void*))client_eof, client);
} else {
ICECAST_LOG_DEBUG("Looking for body EOF without encoding (client=%p)", client);
ret = client_eof(client);
}
ICECAST_LOG_DEBUG("... result is: %i (client=%p)", ret, client);
return ret;
}
client_slurp_result_t client_body_slurp(client_t *client, void *buf, size_t *len)
{
if (!client || !buf || !len)
return CLIENT_SLURP_ERROR;
if (client->request_body_length != -1) {
/* non-streaming mode */
size_t left = (size_t)client->request_body_length - client->request_body_read;
if (!left)
return CLIENT_SLURP_SUCCESS;
if (*len < (size_t)client->request_body_length)
return CLIENT_SLURP_BUFFER_TO_SMALL;
if (left > 2048)
left = 2048;
client_body_read(client, buf + client->request_body_read, left);
if ((size_t)client->request_body_length == client->request_body_read) {
*len = client->request_body_read;
return CLIENT_SLURP_SUCCESS;
} else {
return CLIENT_SLURP_NEEDS_MORE_DATA;
}
} else {
/* streaming mode */
size_t left = *len - client->request_body_read;
int ret;
if (left) {
if (left > 2048)
left = 2048;
client_body_read(client, buf + client->request_body_read, left);
}
ret = client_body_eof(client);
switch (ret) {
case 0:
if (*len == client->request_body_read) {
return CLIENT_SLURP_BUFFER_TO_SMALL;
}
return CLIENT_SLURP_NEEDS_MORE_DATA;
break;
case 1:
return CLIENT_SLURP_SUCCESS;
break;
default:
return CLIENT_SLURP_ERROR;
break;
}
}
}
client_slurp_result_t client_body_skip(client_t *client)
{
char buf[2048];
int ret;
ICECAST_LOG_DEBUG("Slurping client %p");
if (!client) {
ICECAST_LOG_DEBUG("Slurping client %p ... failed");
return CLIENT_SLURP_ERROR;
}
if (client->request_body_length != -1) {
size_t left = (size_t)client->request_body_length - client->request_body_read;
if (!left) {
ICECAST_LOG_DEBUG("Slurping client %p ... was a success");
return CLIENT_SLURP_SUCCESS;
}
if (left > sizeof(buf))
left = sizeof(buf);
client_body_read(client, buf, left);
if ((size_t)client->request_body_length == client->request_body_read) {
ICECAST_LOG_DEBUG("Slurping client %p ... was a success");
return CLIENT_SLURP_SUCCESS;
} else {
ICECAST_LOG_DEBUG("Slurping client %p ... needs more data");
return CLIENT_SLURP_NEEDS_MORE_DATA;
}
} else {
client_body_read(client, buf, sizeof(buf));
}
ret = client_body_eof(client);
switch (ret) {
case 0:
ICECAST_LOG_DEBUG("Slurping client %p ... needs more data");
return CLIENT_SLURP_NEEDS_MORE_DATA;
break;
case 1:
ICECAST_LOG_DEBUG("Slurping client %p ... was a success");
return CLIENT_SLURP_SUCCESS;
break;
default:
ICECAST_LOG_DEBUG("Slurping client %p ... failed");
return CLIENT_SLURP_ERROR;
break;
}
}
......@@ -43,6 +43,13 @@ typedef enum _reuse_tag {
ICECAST_REUSE_UPGRADETLS
} reuse_t;
typedef enum {
CLIENT_SLURP_ERROR,
CLIENT_SLURP_NEEDS_MORE_DATA,
CLIENT_SLURP_BUFFER_TO_SMALL,
CLIENT_SLURP_SUCCESS
} client_slurp_result_t;
struct _client_tag {
/* mode of operation for this client */
operation_mode mode;
......@@ -62,6 +69,14 @@ struct _client_tag {
/* protocol client uses */
protocol_t protocol;
/* http request body length
* -1 for streaming (e.g. chunked), 0 for no body, >0 for NNN bytes
*/
ssize_t request_body_length;
/* http request body length read so far */
size_t request_body_read;
/* http response code for this client */
int respcode;
......@@ -122,5 +137,9 @@ admin_format_t client_get_admin_format_by_content_negotiation(client_t *client);
int client_send_bytes (client_t *client, const void *buf, unsigned len);
int client_read_bytes (client_t *client, void *buf, unsigned len);
void client_set_queue (client_t *client, refbuf_t *refbuf);
ssize_t client_body_read(client_t *client, void *buf, size_t len);
int client_body_eof(client_t *client);
client_slurp_result_t client_body_slurp(client_t *client, void *buf, size_t *len);
client_slurp_result_t client_body_skip(client_t *client);
#endif /* __CLIENT_H__ */
Subproject commit fca416b126cb842034ac3468362c044895975b5a
Subproject commit 9bfb3a34fc41cc8e0075328d7d6527bd84eb40ba
......@@ -79,23 +79,21 @@
typedef struct client_queue_tag {
client_t *client;
int offset;
int stream_offset;
int shoutcast;
char *shoutcast_mount;
char *bodybuffer;
size_t bodybufferlen;
int tried_body;
struct client_queue_tag *next;
} client_queue_t;
typedef struct _thread_queue_tag {
thread_type *thread_id;
struct _thread_queue_tag *next;
} thread_queue_t;
static spin_t _connection_lock; // protects _current_id, _con_queue, _con_queue_tail
static volatile unsigned long _current_id = 0;
static int _initialized = 0;
static volatile client_queue_t *_req_queue = NULL, **_req_queue_tail = &_req_queue;
static volatile client_queue_t *_con_queue = NULL, **_con_queue_tail = &_con_queue;
static volatile client_queue_t *_body_queue = NULL, **_body_queue_tail = &_body_queue;
static int tls_ok;
static tls_ctx_t *tls_ctx;
......@@ -120,6 +118,8 @@ void connection_initialize(void)
_req_queue_tail = &_req_queue;
_con_queue = NULL;
_con_queue_tail = &_con_queue;
_body_queue = NULL;
_body_queue_tail = &_body_queue;
_initialized = 1;
}
......@@ -271,6 +271,12 @@ void connection_uses_tls(connection_t *con)
if (con->tls)
return;
if (con->readbufferlen) {
ICECAST_LOG_ERROR("Connection is now using TLS but has data put back. BAD. Discarding putback data.");
free(con->readbuffer);
con->readbufferlen = 0;
}
con->tlsmode = ICECAST_TLSMODE_RFC2818;
con->read = connection_read_tls;
con->send = connection_send_tls;
......@@ -282,7 +288,71 @@ void connection_uses_tls(connection_t *con)
ssize_t connection_read_bytes(connection_t *con, void *buf, size_t len)
{
return con->read(con, buf, len);
ssize_t done = 0;
ssize_t ret;
if (con->readbufferlen) {
ICECAST_LOG_DEBUG("On connection %p we read from putback buffer, filled with %zu bytes, requested are %zu bytes", con, con->readbufferlen, len);
if (len >= con->readbufferlen) {
memcpy(buf, con->readbuffer, con->readbufferlen);
free(con->readbuffer);
ICECAST_LOG_DEBUG("New fill in buffer=<empty>");
if (len == con->readbufferlen) {
con->readbufferlen = 0;
return len;
} else {
len -= con->readbufferlen;
buf += con->readbufferlen;
done = con->readbufferlen;
con->readbufferlen = 0;
}
} else {
memcpy(buf, con->readbuffer, len);
memmove(con->readbuffer, con->readbuffer+len, con->readbufferlen-len);
con->readbufferlen -= len;
return len;
}
}
ret = con->read(con, buf, len);
if (ret < 0) {
if (done == 0) {
return ret;
} else {
return done;
}
}
return done + ret;
}
int connection_read_put_back(connection_t *con, const void *buf, size_t len)
{
void *n;
if (con->readbufferlen) {
n = realloc(con->readbuffer, con->readbufferlen + len);
if (!n)
return -1;
memcpy(n + con->readbufferlen, buf, len);
con->readbuffer = n;
con->readbufferlen += len;
ICECAST_LOG_DEBUG("On connection %p %zu bytes have been put back.", con, len);
return 0;
} else {
n = malloc(len);
if (!n)
return -1;
memcpy(n, buf, len);
con->readbuffer = n;
con->readbufferlen = len;
ICECAST_LOG_DEBUG("On connection %p %zu bytes have been put back.", con, len);
return 0;
}
}
static sock_t wait_for_serversock(int timeout)
......@@ -466,6 +536,7 @@ static void process_request_queue (void)
}
if (len > 0) {
ssize_t stream_offset = -1;
int pass_it = 1;
char *ptr;
......@@ -487,23 +558,27 @@ static void process_request_queue (void)
* http style headers, we don't want to lose those */
ptr = strstr(client->refbuf->data, "\r\r\n\r\r\n");
if (ptr) {
node->stream_offset = (ptr+6) - client->refbuf->data;
stream_offset = (ptr+6) - client->refbuf->data;
break;
}
ptr = strstr(client->refbuf->data, "\r\n\r\n");
if (ptr) {
node->stream_offset = (ptr+4) - client->refbuf->data;
stream_offset = (ptr+4) - client->refbuf->data;
break;
}
ptr = strstr(client->refbuf->data, "\n\n");
if (ptr) {
node->stream_offset = (ptr+2) - client->refbuf->data;
stream_offset = (ptr+2) - client->refbuf->data;
break;
}
pass_it = 0;
} while (0);
if (pass_it) {
if (stream_offset != -1) {
connection_read_put_back(client->con, client->refbuf->data + stream_offset, node->offset - stream_offset);
node->offset = stream_offset;
}
if ((client_queue_t **)_req_queue_tail == &(node->next))
_req_queue_tail = (volatile client_queue_t **)node_ref;
*node_ref = node->next;
......@@ -526,6 +601,96 @@ static void process_request_queue (void)
_handle_connection();
}
/* add client to body queue.
*/
static void _add_body_client(client_queue_t *node)
{
ICECAST_LOG_DEBUG("Putting client %p in body queue.", node->client);
thread_spin_lock(&_connection_lock);
*_body_queue_tail = node;
_body_queue_tail = (volatile client_queue_t **) &node->next;
thread_spin_unlock(&_connection_lock);
}
static client_slurp_result_t process_request_body_queue_one(client_queue_t *node, time_t timeout, size_t body_size_limit)
{
client_t *client = node->client;
client_slurp_result_t res;
if (client->parser->req_type == httpp_req_post) {
if (node->bodybuffer == NULL && client->request_body_read == 0) {
if (client->request_body_length < 0) {
node->bodybufferlen = body_size_limit;
node->bodybuffer = malloc(node->bodybufferlen);
} else if (client->request_body_length <= (ssize_t)body_size_limit) {
node->bodybufferlen = client->request_body_length;
node->bodybuffer = malloc(node->bodybufferlen);
}
}
}
if (node->bodybuffer) {
res = client_body_slurp(client, node->bodybuffer, &(node->bodybufferlen));
if (res == CLIENT_SLURP_SUCCESS) {
httpp_parse_postdata(client->parser, node->bodybuffer, node->bodybufferlen);
free(node->bodybuffer);
node->bodybuffer = NULL;
}
} else {
res = client_body_skip(client);
}
if (res != CLIENT_SLURP_SUCCESS) {
if (client->con->con_time <= timeout || client->request_body_read >= body_size_limit) {
return CLIENT_SLURP_ERROR;
}
}
return res;
}
/* This queue reads data from the body of clients. */
static void process_request_body_queue (void)
{
client_queue_t **node_ref = (client_queue_t **)&_body_queue;
ice_config_t *config;
time_t timeout;
size_t body_size_limit;
ICECAST_LOG_DEBUG("Processing body queue.");
ICECAST_LOG_DEBUG("_body_queue=%p, &_body_queue=%p, _body_queue_tail=%p", _body_queue, &_body_queue, _body_queue_tail);
config = config_get_config();
timeout = time(NULL) - config->body_timeout;
body_size_limit = config->body_size_limit;
config_release_config();
while (*node_ref) {
client_queue_t *node = *node_ref;
client_t *client = node->client;
client_slurp_result_t res;
node->tried_body = 1;
ICECAST_LOG_DEBUG("Got client %p in body queue.", client);
res = process_request_body_queue_one(node, timeout, body_size_limit);
if (res != CLIENT_SLURP_NEEDS_MORE_DATA) {
ICECAST_LOG_DEBUG("Putting client %p back in connection queue.", client);
if ((client_queue_t **)_body_queue_tail == &(node->next))
_body_queue_tail = (volatile client_queue_t **)node_ref;
*node_ref = node->next;
node->next = NULL;
_add_connection(node);
continue;
}
node_ref = &node->next;
}
}
/* add node to the queue of requests. This is where the clients are when
* initial http details are read.
......@@ -621,6 +786,7 @@ void connection_accept_loop(void)
duration = 300; /* use longer timeouts when nothing waiting */
}
process_request_queue();
process_request_body_queue();
}
/* Give all the other threads notification to shut down */
......@@ -776,8 +942,7 @@ static inline void source_startup(client_t *client, const char *uri)
ret = util_http_build_header(ok->data, PER_CLIENT_REFBUF_SIZE, 0, 0, status_to_send, NULL, NULL, NULL, NULL, NULL, client);
snprintf(ok->data + ret, PER_CLIENT_REFBUF_SIZE - ret, "Content-Length: 0\r\n\r\n");
ok->len = strlen(ok->data);
/* we may have unprocessed data read in, so don't overwrite it */
ok->associated = client->refbuf;
refbuf_release(client->refbuf);
client->refbuf = ok;
fserve_add_client_callback(client, source_client_callback, source);
}
......@@ -1042,14 +1207,7 @@ static void _handle_shoutcast_compatible(client_queue_t *node)
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
if (httpp_parse(parser, http_compliant, strlen(http_compliant))) {
/* we may have more than just headers, so prepare for it */
if (node->stream_offset == node->offset) {
client->refbuf->len = 0;
} else {
char *ptr = client->refbuf->data;
client->refbuf->len = node->offset - node->stream_offset;
memmove(ptr, ptr + node->stream_offset, client->refbuf->len);
}
client->refbuf->len = 0;
client->parser = parser;
client->protocol = ICECAST_PROTOCOL_SHOUTCAST;
node->shoutcast = 0;
......@@ -1236,6 +1394,7 @@ static void _handle_authed_client(client_t *client, void *uri, auth_result resul
_handle_stats_request(client, uri);
break;
case httpp_req_get:
case httpp_req_post:
case httpp_req_options:
_handle_get_request(client, uri);
break;
......@@ -1289,7 +1448,7 @@ static void _handle_authentication_mount_generic(client_t *client, void *uri, mo
if (!mountproxy) {
int command_type = admin_get_command_type(client->admin_command);
if (command_type == ADMINTYPE_MOUNT || command_type == ADMINTYPE_HYBRID) {
const char *mount = httpp_get_query_param(client->parser, "mount");
const char *mount = httpp_get_param(client->parser, "mount");
if (mount)
mountproxy = __find_non_admin_mount(config, mount, type);
}
......@@ -1365,6 +1524,75 @@ static void __prepare_shoutcast_admin_cgi_request(client_t *client)
global_unlock();
}
static void _update_client_request_body_length(client_t *client)
{
const char *header;
long long unsigned int scannumber;
int have = 0;
if (!have) {
if (client->parser->req_type == httpp_req_source) {
client->request_body_length = -1; /* streaming */
have = 1;
}
}
if (!have) {
header = httpp_getvar(client->parser, "transfer-encoding");
if (header) {
if (strcasecmp(header, "identity") != 0) {
client->request_body_length = -1; /* streaming */
have = 1;
}
}
}
if (!have) {
header = httpp_getvar(client->parser, "content-length");
if (header) {
if (sscanf(header, "%llu", &scannumber) == 1) {
client->request_body_length = scannumber;
have = 1;
}
}
}