mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 19:16:51 +02:00
- Return HTTP/1.1 compliant response headers plus some always useful headers
- Return HTTP URL parameters ending with ".dlna.org" as response headers - Store HTTP URL parameters in a map
This commit is contained in:
parent
be9da74958
commit
c267b585fd
3
HISTORY
3
HISTORY
@ -1,6 +1,9 @@
|
||||
VDR Plugin 'streamdev' Revision History
|
||||
---------------------------------------
|
||||
|
||||
- Return HTTP/1.1 compliant response headers plus some always useful headers
|
||||
- Return HTTP URL parameters ending with ".dlna.org" as response headers
|
||||
- Store HTTP URL parameters in a map
|
||||
- Support HTTP HEAD requests with external remuxer
|
||||
- Fixed always using priority 0 for HTTP HEAD requests
|
||||
- Start writer right after creating it
|
||||
|
@ -3,6 +3,8 @@
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "server/connectionHTTP.h"
|
||||
#include "server/menuHTTP.h"
|
||||
@ -48,9 +50,24 @@ bool cConnectionHTTP::Command(char *Cmd)
|
||||
*v = 0;
|
||||
SetHeader("REQUEST_METHOD", Cmd);
|
||||
q = strchr(p, '?');
|
||||
if (q)
|
||||
if (q) {
|
||||
*q = 0;
|
||||
SetHeader("QUERY_STRING", q ? ++q : "");
|
||||
SetHeader("QUERY_STRING", q + 1);
|
||||
while (q++) {
|
||||
char *n = strchr(q, '&');
|
||||
if (n)
|
||||
*n = 0;
|
||||
char *e = strchr(q, '=');
|
||||
if (e)
|
||||
*e++ = 0;
|
||||
else
|
||||
e = n ? n : v;
|
||||
m_Params.insert(tStrStr(q, e));
|
||||
q = n;
|
||||
}
|
||||
}
|
||||
else
|
||||
SetHeader("QUERY_STRING", "");
|
||||
SetHeader("PATH_INFO", p);
|
||||
m_Status = hsHeaders;
|
||||
return true;
|
||||
@ -133,10 +150,7 @@ bool cConnectionHTTP::ProcessRequest(void)
|
||||
}
|
||||
if (!authOk) {
|
||||
isyslog("streamdev-server: HTTP authorization required");
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 401 Authorization Required")
|
||||
&& Respond("WWW-authenticate: basic Realm=\"Streamdev-Server\")")
|
||||
&& Respond("");
|
||||
return HttpResponse(401, true, NULL, "WWW-authenticate: basic Realm=\"Streamdev-Server\"");
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,30 +176,19 @@ bool cConnectionHTTP::ProcessRequest(void)
|
||||
if (m_StreamType == stEXT) {
|
||||
return Respond("HTTP/1.0 200 OK");
|
||||
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("icy-name: %s", true, m_Channel->Name())
|
||||
&& Respond("");
|
||||
return HttpResponse(200, false, "audio/mpeg", "icy-name: %s", m_Channel->Name());
|
||||
} else if (ISRADIO(m_Channel)) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("");
|
||||
return HttpResponse(200, false, "audio/mpeg");
|
||||
} else {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: video/mpeg")
|
||||
&& Respond("");
|
||||
return HttpResponse(200, false, "video/mpeg");
|
||||
}
|
||||
}
|
||||
DELETENULL(m_LiveStreamer);
|
||||
}
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 503 Service unavailable")
|
||||
&& Respond("");
|
||||
return HttpResponse(503, true);
|
||||
}
|
||||
else {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 404 not found")
|
||||
&& Respond("");
|
||||
return HttpResponse(404, true);
|
||||
}
|
||||
} else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) {
|
||||
if (m_ChannelList) {
|
||||
@ -199,37 +202,77 @@ bool cConnectionHTTP::ProcessRequest(void)
|
||||
m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||
return Respond("HTTP/1.0 200 OK");
|
||||
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("icy-name: %s", true, m_Channel->Name())
|
||||
&& Respond("");
|
||||
return HttpResponse(200, true, "audio/mpeg", "icy-name: %s", m_Channel->Name());
|
||||
} else if (ISRADIO(m_Channel)) {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("");
|
||||
return HttpResponse(200, true, "audio/mpeg");
|
||||
} else {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: video/mpeg")
|
||||
&& Respond("");
|
||||
return HttpResponse(200, true, "video/mpeg");
|
||||
}
|
||||
}
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 503 Service unavailable")
|
||||
&& Respond("");
|
||||
return HttpResponse(503, true);
|
||||
}
|
||||
else {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 404 not found")
|
||||
&& Respond("");
|
||||
return HttpResponse(404, true);
|
||||
}
|
||||
}
|
||||
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 400 Bad Request")
|
||||
&& Respond("");
|
||||
return HttpResponse(400, true);
|
||||
}
|
||||
|
||||
static const char *AAA[] = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
static const char *MMM[] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
bool cConnectionHTTP::HttpResponse(int Code, bool Last, const char* ContentType, const char* Headers, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, Headers);
|
||||
cString headers = cString::sprintf(Headers, ap);
|
||||
va_end(ap);
|
||||
|
||||
bool rc;
|
||||
if (Last)
|
||||
DeferClose();
|
||||
switch (Code)
|
||||
{
|
||||
case 200: rc = Respond("HTTP/1.1 200 OK"); break;
|
||||
case 400: rc = Respond("HTTP/1.1 400 Bad Request"); break;
|
||||
case 401: rc = Respond("HTTP/1.1 401 Authorization Required"); break;
|
||||
case 404: rc = Respond("HTTP/1.1 404 Not Found"); break;
|
||||
case 503: rc = Respond("HTTP/1.1 503 Service Unavailable"); break;
|
||||
default: rc = Respond("HTTP/1.1 500 Internal Server Error");
|
||||
}
|
||||
if (rc && ContentType)
|
||||
rc = Respond("Content-Type: %s", true, ContentType);
|
||||
|
||||
if (rc)
|
||||
rc = Respond("Connection: close")
|
||||
&& Respond("Pragma: no-cache")
|
||||
&& Respond("Cache-Control: no-cache");
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm *gmt = gmtime(&t);
|
||||
if (rc && gmt) {
|
||||
char buf[] = "Date: AAA, DD MMM YYYY HH:MM:SS GMT";
|
||||
if (snprintf(buf, sizeof(buf), "Date: %s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", AAA[gmt->tm_wday], gmt->tm_mday, MMM[gmt->tm_mon], gmt->tm_year + 1900, gmt->tm_hour, gmt->tm_min, gmt->tm_sec) == sizeof(buf) - 1)
|
||||
rc = Respond(buf);
|
||||
}
|
||||
|
||||
if (rc && strlen(Headers) > 0)
|
||||
rc = Respond(headers);
|
||||
|
||||
tStrStrMap::iterator it = m_Params.begin();
|
||||
while (rc && it != m_Params.end()) {
|
||||
static const char DLNA_POSTFIX[] = ".dlna.org";
|
||||
if (it->first.rfind(DLNA_POSTFIX) + sizeof(DLNA_POSTFIX) - 1 == it->first.length())
|
||||
rc = Respond("%s: %s", true, it->first.c_str(), it->second.c_str());
|
||||
++it;
|
||||
}
|
||||
return rc && Respond("");
|
||||
}
|
||||
|
||||
void cConnectionHTTP::Flushed(void)
|
||||
@ -263,21 +306,15 @@ void cConnectionHTTP::Flushed(void)
|
||||
|
||||
cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const
|
||||
{
|
||||
// keys for Headers() hash
|
||||
const static std::string QUERY_STRING("QUERY_STRING");
|
||||
const static std::string HOST("HTTP_HOST");
|
||||
|
||||
tStrStrMap::const_iterator it_query = Headers().find(QUERY_STRING);
|
||||
const std::string& query = it_query == Headers().end() ? "" : it_query->second;
|
||||
|
||||
std::string groupTarget;
|
||||
cChannelIterator *iterator = NULL;
|
||||
|
||||
const static std::string GROUP("group");
|
||||
if (Filebase.compare("tree") == 0) {
|
||||
const cChannel* c = NULL;
|
||||
size_t groupIndex = query.find("group=");
|
||||
if (groupIndex != std::string::npos)
|
||||
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
|
||||
tStrStrMap::const_iterator it = m_Params.find(GROUP);
|
||||
if (it != m_Params.end())
|
||||
c = cChannelList::GetGroup(atoi(it->second.c_str()));
|
||||
iterator = new cListTree(c);
|
||||
groupTarget = Filebase + Fileext;
|
||||
} else if (Filebase.compare("groups") == 0) {
|
||||
@ -285,9 +322,9 @@ cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, co
|
||||
groupTarget = (std::string) "group" + Fileext;
|
||||
} else if (Filebase.compare("group") == 0) {
|
||||
const cChannel* c = NULL;
|
||||
size_t groupIndex = query.find("group=");
|
||||
if (groupIndex != std::string::npos)
|
||||
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
|
||||
tStrStrMap::const_iterator it = m_Params.find(GROUP);
|
||||
if (it != m_Params.end())
|
||||
c = cChannelList::GetGroup(atoi(it->second.c_str()));
|
||||
iterator = new cListGroup(c);
|
||||
} else if (Filebase.compare("channels") == 0) {
|
||||
iterator = new cListChannels();
|
||||
@ -299,11 +336,13 @@ cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, co
|
||||
if (iterator) {
|
||||
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) {
|
||||
std::string self = Filebase + Fileext;
|
||||
const std::string& query = Headers().at("QUERY_STRING");
|
||||
if (!query.empty())
|
||||
self += '?' + query;
|
||||
return new cHtmlChannelList(iterator, m_StreamType, self.c_str(), groupTarget.c_str());
|
||||
} else if (Fileext.compare(".m3u") == 0) {
|
||||
std::string base;
|
||||
const static std::string HOST("HTTP_HOST");
|
||||
tStrStrMap::const_iterator it = Headers().find(HOST);
|
||||
if (it != Headers().end())
|
||||
base = "http://" + it->second + "/";
|
||||
|
@ -26,6 +26,7 @@ private:
|
||||
|
||||
std::string m_Authorization;
|
||||
eHTTPStatus m_Status;
|
||||
tStrStrMap m_Params;
|
||||
// job: transfer
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
const cChannel *m_Channel;
|
||||
@ -37,6 +38,8 @@ private:
|
||||
|
||||
cChannelList* ChannelListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
|
||||
bool ProcessURI(const std::string &PathInfo);
|
||||
bool HttpResponse(int Code, bool Last, const char* ContentType = NULL, const char* Headers = "", ...);
|
||||
//__attribute__ ((format (printf, 5, 6)));
|
||||
protected:
|
||||
bool ProcessRequest(void);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user