diff options
| author | phintuka <phintuka> | 2007-01-01 00:07:55 +0000 |
|---|---|---|
| committer | phintuka <phintuka> | 2007-01-01 00:07:55 +0000 |
| commit | 760849ba22222ab400d3282d39b6b7caa32c1125 (patch) | |
| tree | 7d74d1ebe7bdb0549e881b3eb31c267a0f237ee3 | |
| parent | 389abcc9e05d1a7166489469fc6c28d8b343b39c (diff) | |
| download | xineliboutput-760849ba22222ab400d3282d39b6b7caa32c1125.tar.gz xineliboutput-760849ba22222ab400d3282d39b6b7caa32c1125.tar.bz2 | |
Initial import
| -rw-r--r-- | tools/http.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/tools/http.c b/tools/http.c new file mode 100644 index 00000000..cc3d17c6 --- /dev/null +++ b/tools/http.c @@ -0,0 +1,328 @@ +/* + * http.c: HTTP (/RTSP) helper classes + * + * See the main source file 'xineliboutput.c' for copyright information and + * how to reach the author. + * + * $Id: http.c,v 1.1 2007-01-01 00:07:55 phintuka Exp $ + * + */ + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <string.h> + +#include <vdr/config.h> +#include <vdr/tools.h> + +#include "../logdefs.h" + +#include "http.h" + +// +// cHttpReq +// + +bool cHttpReq::SetCommand(const char *Command) +{ + char *tmp = strdup(Command); + char *pt = strchr(tmp, ' '), *uri; + + m_Valid = false; + if(pt) { + *pt = 0; + m_Name = tmp; + + while(*pt && *pt == ' ') pt++; + + uri = pt; + pt = strrchr(uri, ' '); + if(pt) { + m_Version = pt+1; + while(*pt && *pt == ' ') *pt-- = 0; + m_Uri = uri; + m_Valid = true; + } + } + + free(tmp); + return m_Valid; +} + +cHeader *cHttpReq::Header(const char *Name) +{ + for(cHeader *i = m_Headers.First(); i; i = m_Headers.Next(i)) + if(!strcmp(Name, i->Name())) + return i; + return NULL; +} + +void cHttpReq::AddHeader(const char *Header, bool Duplicate) +{ + if(strlen(Header) < 4096) { + char *name = strdup(Header); + char *val = strchr(name, ':'); + if(val) { + *val++ = 0; + AddHeader(name, val, Duplicate); + } + free(name); + } else { + LOGMSG("cConnState::AddHeader: header length exceeds 4096 !"); + } +} + +void cHttpReq::AddHeader(const char *Name, const char *Value, bool Duplicate) +{ + if(strlen(Name) > 64 || strlen(Value) > 4096) { + LOGMSG("cConnState::AddHeader: header length exceeds limit !"); + } else { + cHeader *h = Header(Name); + if(!Duplicate && h) + h->SetValue(Value); + else { + if(m_Headers.Count() < 50) + m_Headers.Add(new cHeader(Name, Value)); + else + LOGMSG("cConnState::AddHeader: header count exceeds 50 !"); + } + } +} + +void cHttpReq::Reset(void) +{ + m_Name = NULL; + m_Uri = NULL; + m_Version = NULL; + m_Valid = false; + m_Headers.Clear(); +} + +// +// Map file extensions to mime types +// + +static const char *mimetype(const char *ext) +{ + static const struct { + char *ext; + char *mime; + } ext2mime[] = { + {"avi", "video/avi"}, + {"vob", "video/mpeg"}, + {"mpg", "video/mpeg"}, + {"mpeg", "video/mpeg"}, + {"vdr", "video/mp2p"}, + + {"mp3", "audio/mp3"}, + {"flac", "audio/flac"}, + + {"jpg", "image/jpeg"}, + {"jpeg", "image/jpeg"}, + {"gif", "image/gif"}, + + {NULL, NULL} + }; + + int i = -1; + while(ext2mime[++i].ext) + if(!strcmp(ext, ext2mime[i].ext)) + return ext2mime[i].mime; + return NULL; +} + +// +// cHttpStreamer +// + +#include "backgroundwriter.h" + +cList<cHttpStreamer> cHttpStreamer::m_Streamers; + +void cHttpStreamer::CloseAll(bool OnlyFinished) +{ + if(!OnlyFinished) { + while(m_Streamers.First()) + m_Streamers.Del(m_Streamers.First()); + + } else { + /* purge finished streamers from list */ + cHttpStreamer *it = m_Streamers.First(); + while(it) { + if(it->Active()) { + it = (cHttpStreamer*)it->Next(); + } else { + m_Streamers.Del(it); + it = m_Streamers.First(); + } + } + } +} + +cHttpStreamer::cHttpStreamer(int fd_http, const char *filename, const char *Range) +{ + m_fds.set_handle(fd_http); + m_fdf = -1; + + m_Filename = filename; + m_FileSize = -1; + m_Start = 0; + m_End = -1; + + m_ConnState = NULL; + + m_Finished = false; + + CloseAll(true); + + m_Writer = new cBackgroundWriter(fd_http, KILOBYTE(128), true); + m_Streamers.Add(this); + + if(m_Streamers.Count() > 5) { + LOGMSG("WARNING: There are %d running HTTP streamers !", m_Streamers.Count()); + if(m_Streamers.Count() > 20) { + errno = 0; + LOGERR("ERROR: There are %d running HTTP streamers, cancelling first", + m_Streamers.Count()); + m_Streamers.Del(m_Streamers.First()); + } + } + + ParseRange(Range); + + Start(); +} + +cHttpStreamer::~cHttpStreamer() +{ + Cancel(3); + if(m_Writer) + delete m_Writer; + if(m_ConnState) + delete m_ConnState; + if(m_fdf >= 0) + close(m_fdf); + m_fdf = -1; +} + +void cHttpStreamer::ParseRange(const char *Range) +{ + m_Start = 0; + m_End = -1; + if(Range) { + LOGMSG("Range: %s", Range); + switch(sscanf(Range, "%" PRId64 "-%" PRId64, &m_Start, &m_End)) { + case 2: LOGMSG("Range: %s (%" PRId64 " - %" PRId64 ")", Range, m_Start, m_End); + break; + case 1: m_End = -1; + LOGMSG("Range: %s (%" PRId64 " -)", Range, m_Start); + case 0: m_Start = 0; + default: break; + } + } +} + +bool cHttpStreamer::Seek(void) +{ + if(m_fdf < 0) { + m_fdf = open(m_Filename, O_RDONLY); + if(m_fdf < 0) { + LOGERR("cHttpStreamer: error opening %s", *m_Filename); + + m_fds.write_cmd("HTTP/1.1 401 Not Found\r\n"); + m_fds.write_cmd("Connection: Close\r\n"); + m_fds.write_cmd("\r\n"); + return false; + } + } + + off_t FileSize = lseek(m_fdf, 0, SEEK_END); + if(m_Start >= FileSize) + m_Start = FileSize - 1; + + if(m_Start > 0) { + if(m_End >= FileSize) + m_End = FileSize - 1; + + m_fds.write_cmd("HTTP/1.1 206 Partial Content\r\n"); + if(m_End <= 0) + m_fds.printf("Range: %" PRId64 "-\r\n", m_Start); + else + m_fds.printf("Range: %" PRId64 "-%" PRId64 "\r\n", m_Start, m_End); + } else { + m_fds.write_cmd("HTTP/1.1 200 OK\r\n"); + } + + /* content type */ + char *ext = strrchr(m_Filename, '.'); + if(ext) { + const char *mime = mimetype(ext+1); + if(mime) + m_fds.printf("Content-Type: %s\r\n", mime); + } + + /* Content-Length */ + if(m_FileSize >= 0) { + int64_t len = m_FileSize; + if(m_End >= 0) + len = m_End + 1; + else if(m_Start >= 0) + len -= m_Start; + m_fds.printf("Content-Length: %" PRId64 "\r\n", len); + } + + /* Connection */ + m_fds.write_cmd("Connection: Keep-Alive\r\n"); + + /* End of reply */ + m_fds.write_cmd("\r\n"); + + if(m_Start) + lseek(m_fdf, (off_t)m_Start, SEEK_SET); + else + lseek(m_fdf, 0, SEEK_SET); + + return true; +} + +bool cHttpStreamer::ReadPipelined(void) +{ + char buf[2048]; + int r; + + if(m_ConnState) + delete m_ConnState; + m_ConnState = new cConnState; + + do { + r = m_fds.readline(buf, sizeof(buf), 100); + if(r < 0 || errno == EAGAIN || r >= (int)sizeof(buf)) + return false; + + LOGMSG("HTTP pipelined: %s", buf); + + if(!*m_ConnState->Name()) { + if(!m_ConnState->SetCommand(buf) || + !strcmp(m_ConnState->Name(), "GET") || + !strncmp(m_ConnState->Uri(), "/PLAYFILE", 9)) { + LOGMSG("Incorrect HTTP request: %s", buf); + return false; + } + } + else if(r > 0) + m_ConnState->AddHeader(buf); + } while(r>0); + + if(m_ConnState->Header("Range")) + ParseRange(m_ConnState->Header("Range")->Value()); + + return true; +} + +void cHttpStreamer::Action(void) +{ + // not implemented + + m_Finished = true; +} |
