Newer
Older
Fabrice Bellard
committed
if (ret < 0)
return -1;
} else if (ptr >= c->buffer_end) {
/* request too long: cannot do anything */
return -1;
Giancarlo Formicuccia
committed
} else goto read_loop;
}
break;
case HTTPSTATE_SEND_HEADER:
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
Fabrice Bellard
committed
/* no need to write if no events */
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
goto close_connection;
if (c->stream)
c->stream->bytes_served += len;
c->data_count += len;
Fabrice Bellard
committed
av_freep(&c->pb_buffer);
/* all the buffer was sent : synchronize to the incoming
* stream */
c->state = HTTPSTATE_SEND_DATA_HEADER;
c->buffer_ptr = c->buffer_end = c->buffer;
}
}
break;
case HTTPSTATE_SEND_DATA:
case HTTPSTATE_SEND_DATA_HEADER:
case HTTPSTATE_SEND_DATA_TRAILER:
Fabrice Bellard
committed
/* for packetized output, we consider we can always write (the
input streams set the speed). It may be better to verify
Fabrice Bellard
committed
that we do not rely too much on the kernel queues */
if (!c->is_packetized) {
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
Fabrice Bellard
committed
/* no need to read if no events */
if (!(c->poll_entry->revents & POLLOUT))
return 0;
}
if (http_send_data(c) < 0)
/* close connection if trailer sent */
if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
return -1;
break;
case HTTPSTATE_RECEIVE_DATA:
/* no need to read if no events */
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
if (!(c->poll_entry->revents & POLLIN))
return 0;
if (http_receive_data(c) < 0)
return -1;
break;
case HTTPSTATE_WAIT_FEED:
/* no need to read if no events */
if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
return -1;
/* nothing to do, we'll be waken up by incoming feed packets */
break;
Fabrice Bellard
committed
case RTSPSTATE_SEND_REPLY:
if (c->poll_entry->revents & (POLLERR | POLLHUP))
goto close_connection;
Fabrice Bellard
committed
/* no need to write if no events */
if (!(c->poll_entry->revents & POLLOUT))
return 0;
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
Fabrice Bellard
committed
if (len < 0) {
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
goto close_connection;
Fabrice Bellard
committed
}
} else {
c->buffer_ptr += len;
c->data_count += len;
if (c->buffer_ptr >= c->buffer_end) {
/* all the buffer was sent : wait for a new request */
av_freep(&c->pb_buffer);
start_wait_request(c, 1);
}
}
break;
case RTSPSTATE_SEND_PACKET:
if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
av_freep(&c->packet_buffer);
return -1;
}
/* no need to write if no events */
if (!(c->poll_entry->revents & POLLOUT))
return 0;
len = send(c->fd, c->packet_buffer_ptr,
c->packet_buffer_end - c->packet_buffer_ptr, 0);
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
/* error : close connection */
av_freep(&c->packet_buffer);
return -1;
}
} else {
c->packet_buffer_ptr += len;
if (c->packet_buffer_ptr >= c->packet_buffer_end) {
/* all the buffer was sent : wait for a new request */
av_freep(&c->packet_buffer);
c->state = RTSPSTATE_WAIT_REQUEST;
}
}
break;
Fabrice Bellard
committed
case HTTPSTATE_READY:
/* nothing to do */
break;
close_connection:
av_freep(&c->pb_buffer);
return -1;
static int extract_rates(char *rates, int ratelen, const char *request)
{
const char *p;
for (p = request; *p && *p != '\r' && *p != '\n'; ) {
if (av_strncasecmp(p, "Pragma:", 7) == 0) {
const char *q = p + 7;
while (*q && *q != '\n' && av_isspace(*q))
q++;
if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
int stream_no;
int rate_no;
q += 20;
while (1) {
while (*q && *q != '\n' && *q != ':')
q++;
if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
break;
stream_no--;
if (stream_no < ratelen && stream_no >= 0)
rates[stream_no] = rate_no;
while (*q && *q != '\n' && !av_isspace(*q))
q++;
}
return 1;
}
}
p = strchr(p, '\n');
if (!p)
break;
p++;
}
return 0;
}
static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
{
int i;
int best_bitrate = 100000000;
int best = -1;
for (i = 0; i < feed->nb_streams; i++) {
Michael Niedermayer
committed
AVCodecContext *feed_codec = feed->streams[i]->codec;
if (feed_codec->codec_id != codec->codec_id ||
feed_codec->sample_rate != codec->sample_rate ||
feed_codec->width != codec->width ||
feed_codec->height != codec->height)
continue;
/* Potential stream */
/* We want the fastest stream less than bit_rate, or the slowest
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
* faster than bit_rate
*/
if (feed_codec->bit_rate <= bit_rate) {
if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
best_bitrate = feed_codec->bit_rate;
best = i;
}
} else {
if (feed_codec->bit_rate < best_bitrate) {
best_bitrate = feed_codec->bit_rate;
best = i;
}
}
}
return best;
}
static int modify_current_stream(HTTPContext *c, char *rates)
{
int i;
FFStream *req = c->stream;
int action_required = 0;
/* Not much we can do for a feed */
if (!req->feed)
return 0;
for (i = 0; i < req->nb_streams; i++) {
Michael Niedermayer
committed
AVCodecContext *codec = req->streams[i]->codec;
switch(rates[i]) {
case 0:
c->switch_feed_streams[i] = req->feed_streams[i];
break;
case 1:
c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
break;
case 2:
/* Wants off or slow */
c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
#ifdef WANTS_OFF
/* This doesn't work well when it turns off the only stream! */
c->switch_feed_streams[i] = -2;
c->feed_streams[i] = -2;
#endif
break;
}
if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
action_required = 1;
}
Fabrice Bellard
committed
/* XXX: factorize in utils.c ? */
/* XXX: take care with different space meaning */
static void skip_spaces(const char **pp)
{
const char *p;
p = *pp;
while (*p == ' ' || *p == '\t')
p++;
*pp = p;
}
static void get_word(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
p = *pp;
skip_spaces(&p);
q = buf;
while (!av_isspace(*p) && *p != '\0') {
Fabrice Bellard
committed
if ((q - buf) < buf_size - 1)
*q++ = *p;
p++;
}
if (buf_size > 0)
*q = '\0';
*pp = p;
}
static void get_arg(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
int quote;
p = *pp;
while (av_isspace(*p)) p++;
q = buf;
quote = 0;
if (*p == '\"' || *p == '\'')
quote = *p++;
for(;;) {
if (quote) {
if (*p == quote)
break;
} else {
break;
}
if (*p == '\0')
break;
if ((q - buf) < buf_size - 1)
*q++ = *p;
p++;
}
*q = '\0';
if (quote && *p == quote)
p++;
*pp = p;
}
static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
const char *p, const char *filename, int line_num)
{
char arg[1024];
IPAddressACL acl;
int errors = 0;
get_arg(arg, sizeof(arg), &p);
if (av_strcasecmp(arg, "allow") == 0)
else if (av_strcasecmp(arg, "deny") == 0)
acl.action = IP_DENY;
else {
fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
filename, line_num, arg);
errors++;
}
get_arg(arg, sizeof(arg), &p);
if (resolve_host(&acl.first, arg) != 0) {
fprintf(stderr, "%s:%d: ACL refers to invalid host or IP address '%s'\n",
filename, line_num, arg);
errors++;
} else
acl.last = acl.first;
get_arg(arg, sizeof(arg), &p);
if (arg[0]) {
if (resolve_host(&acl.last, arg) != 0) {
fprintf(stderr, "%s:%d: ACL refers to invalid host or IP address '%s'\n",
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
filename, line_num, arg);
errors++;
}
}
if (!errors) {
IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
IPAddressACL **naclp = 0;
acl.next = 0;
*nacl = acl;
if (stream)
naclp = &stream->acl;
else if (feed)
naclp = &feed->acl;
else if (ext_acl)
naclp = &ext_acl;
else {
fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
filename, line_num);
errors++;
}
if (naclp) {
while (*naclp)
naclp = &(*naclp)->next;
*naclp = nacl;
} else
av_free(nacl);
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
}
}
static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
{
FILE* f;
char line[1024];
char cmd[1024];
IPAddressACL *acl = NULL;
int line_num = 0;
const char *p;
f = fopen(stream->dynamic_acl, "r");
if (!f) {
perror(stream->dynamic_acl);
return NULL;
}
acl = av_mallocz(sizeof(IPAddressACL));
/* Build ACL */
for(;;) {
if (fgets(line, sizeof(line), f) == NULL)
break;
line_num++;
p = line;
p++;
if (*p == '\0' || *p == '#')
continue;
get_arg(cmd, sizeof(cmd), &p);
if (!av_strcasecmp(cmd, "ACL"))
parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
}
fclose(f);
return acl;
}
static void free_acl_list(IPAddressACL *in_acl)
{
IPAddressACL *pacl,*pacl2;
pacl = in_acl;
while(pacl) {
pacl2 = pacl;
pacl = pacl->next;
av_freep(pacl2);
}
}
static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
{
enum IPAddressAction last_action = IP_DENY;
IPAddressACL *acl;
struct in_addr *src = &c->from_addr.sin_addr;
unsigned long src_addr = src->s_addr;
for (acl = in_acl; acl; acl = acl->next) {
if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
return (acl->action == IP_ALLOW) ? 1 : 0;
last_action = acl->action;
}
/* Nothing matched, so return not the last action */
return (last_action == IP_DENY) ? 1 : 0;
}
static int validate_acl(FFStream *stream, HTTPContext *c)
{
int ret = 0;
IPAddressACL *acl;
/* if stream->acl is null validate_acl_list will return 1 */
ret = validate_acl_list(stream->acl, c);
if (stream->dynamic_acl[0]) {
acl = parse_dynamic_acl(stream, c);
ret = validate_acl_list(acl, c);
free_acl_list(acl);
}
return ret;
}
Fabrice Bellard
committed
/* compute the real filename of a file by matching it without its
extensions to all the stream's filenames */
Fabrice Bellard
committed
static void compute_real_filename(char *filename, int max_size)
{
char file1[1024];
char file2[1024];
char *p;
FFStream *stream;
/* compute filename by matching without the file extensions */
av_strlcpy(file1, filename, sizeof(file1));
Fabrice Bellard
committed
p = strrchr(file1, '.');
if (p)
*p = '\0';
for(stream = first_stream; stream != NULL; stream = stream->next) {
av_strlcpy(file2, stream->filename, sizeof(file2));
Fabrice Bellard
committed
p = strrchr(file2, '.');
if (p)
*p = '\0';
if (!strcmp(file1, file2)) {
av_strlcpy(filename, stream->filename, max_size);
Fabrice Bellard
committed
break;
}
}
}
enum RedirType {
REDIR_NONE,
REDIR_ASX,
REDIR_RAM,
REDIR_ASF,
REDIR_RTSP,
REDIR_SDP,
};
/* parse HTTP request and prepare header */
Fabrice Bellard
committed
enum RedirType redir_type;
Alex Beregszaszi
committed
char info[1024], filename[1024];
char url[1024], *q;
char protocol[32];
char msg[1024];
const char *mime_type;
FFStream *stream;
char ratebuf[32];
get_word(cmd, sizeof(cmd), &p);
av_strlcpy(c->method, cmd, sizeof(c->method));
Alex Beregszaszi
committed
c->post = 0;
Alex Beregszaszi
committed
c->post = 1;
get_word(url, sizeof(url), &p);
av_strlcpy(c->url, url, sizeof(c->url));
Fabrice Bellard
committed
get_word(protocol, sizeof(protocol), (const char **)&p);
if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
return -1;
av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
if (ffserver_debug)
http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
/* find the filename and the optional info string in the request */
p1 = strchr(url, '?');
if (p1) {
av_strlcpy(info, p1, sizeof(info));
*p1 = '\0';
av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
Alex Beregszaszi
committed
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
if (*useragent && *useragent != '\n' && av_isspace(*useragent))
useragent++;
break;
}
p = strchr(p, '\n');
if (!p)
break;
p++;
}
Fabrice Bellard
committed
redir_type = REDIR_NONE;
if (av_match_ext(filename, "asx")) {
Fabrice Bellard
committed
redir_type = REDIR_ASX;
filename[strlen(filename)-1] = 'f';
} else if (av_match_ext(filename, "asf") &&
(!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
/* if this isn't WMP or lookalike, return the redirector file */
Fabrice Bellard
committed
redir_type = REDIR_ASF;
} else if (av_match_ext(filename, "rpm,ram")) {
Fabrice Bellard
committed
redir_type = REDIR_RAM;
strcpy(filename + strlen(filename)-2, "m");
} else if (av_match_ext(filename, "rtsp")) {
Fabrice Bellard
committed
redir_type = REDIR_RTSP;
Alex Beregszaszi
committed
compute_real_filename(filename, sizeof(filename) - 1);
} else if (av_match_ext(filename, "sdp")) {
Fabrice Bellard
committed
redir_type = REDIR_SDP;
Alex Beregszaszi
committed
compute_real_filename(filename, sizeof(filename) - 1);
Alex Beregszaszi
committed
// "redirect" / request to index.html
if (!strlen(filename))
av_strlcpy(filename, "index.html", sizeof(filename) - 1);
Alex Beregszaszi
committed
if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
break;
stream = stream->next;
}
if (stream == NULL) {
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "File '%s' not found", url);
http_log("File '%s' not found\n", url);
c->stream = stream;
memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
if (stream->stream_type == STREAM_TYPE_REDIRECT) {
c->http_error = 301;
q = c->buffer;
snprintf(q, c->buffer_size,
"HTTP/1.0 301 Moved\r\n"
"Location: %s\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<html><head><title>Moved</title></head><body>\r\n"
"You should be <a href=\"%s\">redirected</a>.\r\n"
"</body></html>\r\n", stream->feed_filename, stream->feed_filename);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
/* If this is WMP, get the rate information */
if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
}
if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
current_bandwidth += stream->bandwidth;
/* If already streaming this feed, do not let start another feeder. */
if (stream->feed_opened) {
snprintf(msg, sizeof(msg), "This feed is already being received.");
http_log("Feed '%s' already being received\n", stream->feed_filename);
goto send_error;
}
Alex Beregszaszi
committed
if (c->post == 0 && max_bandwidth < current_bandwidth) {
snprintf(q, c->buffer_size,
"HTTP/1.0 503 Server too busy\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<html><head><title>Too busy</title></head><body>\r\n"
"<p>The server is too busy to serve your request at this time.</p>\r\n"
"<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
"and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
"</body></html>\r\n", current_bandwidth, max_bandwidth);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
Fabrice Bellard
committed
if (redir_type != REDIR_NONE) {
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
if (av_strncasecmp(p, "Host:", 5) == 0) {
hostinfo = p + 5;
break;
}
p = strchr(p, '\n');
if (!p)
break;
p++;
}
if (hostinfo) {
char *eoh;
char hostbuf[260];
while (av_isspace(*hostinfo))
hostinfo++;
eoh = strchr(hostinfo, '\n');
if (eoh) {
if (eoh[-1] == '\r')
eoh--;
if (eoh - hostinfo < sizeof(hostbuf) - 1) {
memcpy(hostbuf, hostinfo, eoh - hostinfo);
hostbuf[eoh - hostinfo] = 0;
c->http_error = 200;
q = c->buffer;
Fabrice Bellard
committed
switch(redir_type) {
case REDIR_ASX:
snprintf(q, c->buffer_size,
"HTTP/1.0 200 ASX Follows\r\n"
"Content-type: video/x-ms-asf\r\n"
"\r\n"
"<ASX Version=\"3\">\r\n"
//"<!-- Autogenerated by ffserver -->\r\n"
"<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
"</ASX>\r\n", hostbuf, filename, info);
Fabrice Bellard
committed
break;
case REDIR_RAM:
snprintf(q, c->buffer_size,
"HTTP/1.0 200 RAM Follows\r\n"
"Content-type: audio/x-pn-realaudio\r\n"
"\r\n"
"# Autogenerated by ffserver\r\n"
"http://%s/%s%s\r\n", hostbuf, filename, info);
Fabrice Bellard
committed
break;
case REDIR_ASF:
snprintf(q, c->buffer_size,
"HTTP/1.0 200 ASF Redirect follows\r\n"
"Content-type: video/x-ms-asf\r\n"
"\r\n"
"[Reference]\r\n"
"Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
Fabrice Bellard
committed
break;
case REDIR_RTSP:
{
char hostname[256], *p;
/* extract only hostname */
av_strlcpy(hostname, hostbuf, sizeof(hostname));
Fabrice Bellard
committed
p = strrchr(hostname, ':');
if (p)
*p = '\0';
snprintf(q, c->buffer_size,
"HTTP/1.0 200 RTSP Redirect follows\r\n"
"Content-type: application/x-rtsp\r\n"
"\r\n"
"rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
Fabrice Bellard
committed
}
break;
case REDIR_SDP:
{
int sdp_data_size;
socklen_t len;
Fabrice Bellard
committed
struct sockaddr_in my_addr;
snprintf(q, c->buffer_size,
"HTTP/1.0 200 OK\r\n"
"Content-type: application/sdp\r\n"
"\r\n");
Fabrice Bellard
committed
len = sizeof(my_addr);
/* XXX: Should probably fail? */
if (getsockname(c->fd, (struct sockaddr *)&my_addr, &len))
http_log("getsockname() failed\n");
Fabrice Bellard
committed
/* XXX: should use a dynamic buffer */
sdp_data_size = prepare_sdp_description(stream,
&sdp_data,
Fabrice Bellard
committed
my_addr.sin_addr);
if (sdp_data_size > 0) {
memcpy(q, sdp_data, sdp_data_size);
q += sdp_data_size;
*q = '\0';
av_free(sdp_data);
}
}
break;
default:
Fabrice Bellard
committed
break;
Fabrice Bellard
committed
}
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
}
}
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
goto send_error;
stream->conns_served++;
Alex Beregszaszi
committed
if (c->post) {
/* if post, it means a feed is being sent */
if (!stream->is_feed) {
/* However it might be a status report from WMP! Let us log the
* data as it might come handy one day. */
int client_id = 0;
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
logline = p;
break;
}
if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
client_id = strtol(p + 18, 0, 10);
p = strchr(p, '\n');
if (!p)
break;
p++;
}
if (logline) {
char *eol = strchr(logline, '\n');
logline += 17;
if (eol) {
if (eol[-1] == '\r')
eol--;
c->suppress_log = 1;
}
}
#ifdef DEBUG
http_log("\nGot request:\n%s\n", c->buffer);
#endif
if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
HTTPContext *wmpc;
/* Now we have to find the client_id */
for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
if (wmpc->wmp_client_id == client_id)
break;
}
if (wmpc && modify_current_stream(wmpc, ratebuf))
wmpc->switch_pending = 1;
}
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "POST command not handled");
Philip Gladstone
committed
c->stream = 0;
goto send_error;
}
if (http_start_receive_data(c) < 0) {
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "could not open feed");
goto send_error;
}
c->http_error = 0;
c->state = HTTPSTATE_RECEIVE_DATA;
return 0;
}
#ifdef DEBUG
if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
http_log("\nGot request:\n%s\n", c->buffer);
#endif
/* open input stream */
if (open_input_stream(c, info) < 0) {
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
/* prepare HTTP header */
c->buffer[0] = 0;
av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
mime_type = "application/x-octet-stream";
av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
if (!strcmp(c->stream->fmt->name,"asf_stream")) {
/* Need to allocate a client id */
c->wmp_client_id = av_lfg_get(&random_state);
av_strlcatf(c->buffer, c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
av_strlcatf(c->buffer, c->buffer_size, "\r\n");
q = c->buffer + strlen(c->buffer);
/* prepare output buffer */
c->http_error = 0;
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
send_error:
c->http_error = 404;
q = c->buffer;
Michael Niedermayer
committed
htmlstrip(msg);
snprintf(q, c->buffer_size,
"HTTP/1.0 404 Not Found\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<html>\n"
"<head><title>404 Not Found</title></head>\n"
"<body>%s</body>\n"
"</html>\n", msg);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
send_status:
compute_status(c);
c->http_error = 200; /* horrible : we use this value to avoid
going to the send data state */
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
static void fmt_bytecount(AVIOContext *pb, int64_t count)
static const char suffix[] = " kMGTP";
const char *s;
for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
avio_printf(pb, "%"PRId64"%c", count, *s);
}
static void compute_status(HTTPContext *c)
Fabrice Bellard
committed
char *p;
Fabrice Bellard
committed
int i, len;
if (avio_open_dyn_buf(&pb) < 0) {
Fabrice Bellard
committed
/* XXX: return an error ? */
Fabrice Bellard
committed
c->buffer_end = c->buffer;
return;
avio_printf(pb, "HTTP/1.0 200 OK\r\n");
avio_printf(pb, "Content-type: text/html\r\n");
avio_printf(pb, "Pragma: no-cache\r\n");
avio_printf(pb, "\r\n");
avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
Erik Hovland
committed
if (c->stream->feed_filename[0])
avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
avio_printf(pb, "</head>\n<body>");
avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
avio_printf(pb, "<h2>Available Streams</h2>\n");
avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
avio_printf(pb, "<tr><th valign=top>Path<th align=left>Served<br>Conns<th><br>bytes<th valign=top>Format<th>Bit rate<br>kbits/s<th align=left>Video<br>kbits/s<th><br>Codec<th align=left>Audio<br>kbits/s<th><br>Codec<th align=left valign=top>Feed\n");
char sfilename[1024];
char *eosf;
if (stream->feed != stream) {
av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
eosf = sfilename + strlen(sfilename);
if (eosf - sfilename >= 4) {
if (strcmp(eosf - 4, ".asf") == 0)
strcpy(eosf - 4, ".asx");
else if (strcmp(eosf - 3, ".rm") == 0)
strcpy(eosf - 3, ".ram");
else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
Fabrice Bellard
committed
/* generate a sample RTSP director if
unicast. Generate an SDP redirector if
multicast */
Fabrice Bellard
committed
eosf = strrchr(sfilename, '.');
if (!eosf)
eosf = sfilename + strlen(sfilename);
Fabrice Bellard
committed
if (stream->is_multicast)
strcpy(eosf, ".sdp");
else
strcpy(eosf, ".rtsp");