summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorFrank Schmirler <vdr@schmirler.de>2012-11-16 02:00:09 +0100
committerFrank Schmirler <vdr@schmirler.de>2012-11-16 02:00:09 +0100
commitc267b585fd74c2a72ae683fe71244a085b57b633 (patch)
tree4a0b0065fb1348405b333ef16e41b376d56aec6e /server
parentbe9da74958088474231b080ee1b72f2cb9d69418 (diff)
downloadvdr-plugin-streamdev-c267b585fd74c2a72ae683fe71244a085b57b633.tar.gz
vdr-plugin-streamdev-c267b585fd74c2a72ae683fe71244a085b57b633.tar.bz2
- 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
Diffstat (limited to 'server')
-rw-r--r--server/connectionHTTP.c153
-rw-r--r--server/connectionHTTP.h3
2 files changed, 99 insertions, 57 deletions
diff --git a/server/connectionHTTP.c b/server/connectionHTTP.c
index b9f98b4..27ff779 100644
--- a/server/connectionHTTP.c
+++ b/server/connectionHTTP.c
@@ -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 + "/";
diff --git a/server/connectionHTTP.h b/server/connectionHTTP.h
index 8f071ce..b5b5b86 100644
--- a/server/connectionHTTP.h
+++ b/server/connectionHTTP.h
@@ -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);