HLS/HTTP fixes:

- Optimize URIReader class to not close connections if not needed
- reConnector now works for non-GET requests with GET params
- Chunk sending mode correctly kept for HLS
- Removed lots of H.Clean() from HLS that weren't needed
- Improved HTTP output class request handling logic
- Removed firstRun from HTTP output class; no longer needed
This commit is contained in:
Thulinma 2021-09-20 13:09:27 +02:00
parent 5774ce3b9e
commit 194b6e1388
5 changed files with 13 additions and 27 deletions

View file

@ -148,7 +148,7 @@ std::string &HTTP::Parser::BuildRequest(){
/// \todo Include POST variable handling for vars? /// \todo Include POST variable handling for vars?
std::map<std::string, std::string>::iterator it; std::map<std::string, std::string>::iterator it;
if (protocol.size() < 5 || protocol[4] != '/'){protocol = "HTTP/1.0";} if (protocol.size() < 5 || protocol[4] != '/'){protocol = "HTTP/1.0";}
if (method == "GET" && vars.size() && url.find('?') == std::string::npos){ if (method != "POST" && vars.size() && url.find('?') == std::string::npos){
builder = method + " " + Encodings::URL::encode(url, "/:=@[]") + allVars() + " " + protocol + "\r\n"; builder = method + " " + Encodings::URL::encode(url, "/:=@[]") + allVars() + " " + protocol + "\r\n";
}else{ }else{
builder = method + " " + Encodings::URL::encode(url, "/:=@[]") + " " + protocol + "\r\n"; builder = method + " " + Encodings::URL::encode(url, "/:=@[]") + " " + protocol + "\r\n";
@ -277,9 +277,10 @@ void HTTP::Parser::SendResponse(std::string code, std::string message, Socket::C
void HTTP::Parser::StartResponse(std::string code, std::string message, const HTTP::Parser &request, void HTTP::Parser::StartResponse(std::string code, std::string message, const HTTP::Parser &request,
Socket::Connection &conn, bool bufferAllChunks){ Socket::Connection &conn, bool bufferAllChunks){
std::string prot = request.protocol; std::string prot = request.protocol;
sendingChunks = bool willSendChunks =
(!bufferAllChunks && request.protocol == "HTTP/1.1" && request.GetHeader("Connection") != "close"); (!bufferAllChunks && request.protocol == "HTTP/1.1" && request.GetHeader("Connection") != "close");
CleanPreserveHeaders(); CleanPreserveHeaders();
sendingChunks = willSendChunks;
protocol = prot; protocol = prot;
if (sendingChunks){ if (sendingChunks){
SetHeader("Transfer-Encoding", "chunked"); SetHeader("Transfer-Encoding", "chunked");

View file

@ -45,13 +45,13 @@ namespace HTTP{
void URIReader::dataCallback(const char *ptr, size_t size){allData.append(ptr, size);} void URIReader::dataCallback(const char *ptr, size_t size){allData.append(ptr, size);}
bool URIReader::open(const HTTP::URL &uri){ bool URIReader::open(const HTTP::URL &uri){
close();
myURI = uri; myURI = uri;
curPos = 0; curPos = 0;
allData.truncate(0); allData.truncate(0);
bufPos = 0; bufPos = 0;
if (!myURI.protocol.size() || myURI.protocol == "file"){ if (!myURI.protocol.size() || myURI.protocol == "file"){
close();
if (!myURI.path.size() || myURI.path == "-"){ if (!myURI.path.size() || myURI.path == "-"){
downer.getSocket().open(-1, fileno(stdin)); downer.getSocket().open(-1, fileno(stdin));
stateType = HTTP::Stream; stateType = HTTP::Stream;

View file

@ -230,13 +230,11 @@ namespace Mist{
std::string sessId = H.GetVar("sessId"); std::string sessId = H.GetVar("sessId");
if (H.url == "/crossdomain.xml"){ if (H.url == "/crossdomain.xml"){
H.Clean();
H.SetHeader("Content-Type", "text/xml"); H.SetHeader("Content-Type", "text/xml");
H.SetHeader("Server", APPIDENT); H.SetHeader("Server", APPIDENT);
H.setCORSHeaders(); H.setCORSHeaders();
if (method == "OPTIONS" || method == "HEAD"){ if (method == "OPTIONS" || method == "HEAD"){
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean();
return; return;
} }
H.SetBody("<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy SYSTEM " H.SetBody("<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy SYSTEM "
@ -244,13 +242,11 @@ namespace Mist{
"cross-domain-policy.dtd\"><cross-domain-policy><allow-access-from domain=\"*\" " "cross-domain-policy.dtd\"><cross-domain-policy><allow-access-from domain=\"*\" "
"/><site-control permitted-cross-domain-policies=\"all\"/></cross-domain-policy>"); "/><site-control permitted-cross-domain-policies=\"all\"/></cross-domain-policy>");
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean(); // clean for any possible next requests
return; return;
}// crossdomain.xml }// crossdomain.xml
if (H.method == "OPTIONS"){ if (H.method == "OPTIONS"){
bool isTS = (HTTP::URL(H.url).getExt().substr(0, 3) != "m3u"); bool isTS = (HTTP::URL(H.url).getExt().substr(0, 3) != "m3u");
H.Clean();
H.setCORSHeaders(); H.setCORSHeaders();
if (isTS){ if (isTS){
H.SetHeader("Content-Type", "video/mp2t"); H.SetHeader("Content-Type", "video/mp2t");
@ -266,7 +262,6 @@ namespace Mist{
} }
H.SetBody(""); H.SetBody("");
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean();
return; return;
} }
@ -294,11 +289,9 @@ namespace Mist{
if (sscanf(tmpStr.c_str(), "/%zu_%zu/%" PRIu64 "_%" PRIu64 ".ts", &vidTrack, &audTrack, &from, &until) != 4){ if (sscanf(tmpStr.c_str(), "/%zu_%zu/%" PRIu64 "_%" PRIu64 ".ts", &vidTrack, &audTrack, &from, &until) != 4){
if (sscanf(tmpStr.c_str(), "/%zu/%" PRIu64 "_%" PRIu64 ".ts", &vidTrack, &from, &until) != 3){ if (sscanf(tmpStr.c_str(), "/%zu/%" PRIu64 "_%" PRIu64 ".ts", &vidTrack, &from, &until) != 3){
MEDIUM_MSG("Could not parse URL: %s", H.getUrl().c_str()); MEDIUM_MSG("Could not parse URL: %s", H.getUrl().c_str());
H.Clean();
H.setCORSHeaders(); H.setCORSHeaders();
H.SetBody("The HLS URL wasn't understood - what did you want, exactly?\n"); H.SetBody("The HLS URL wasn't understood - what did you want, exactly?\n");
myConn.SendNow(H.BuildResponse("404", "URL mismatch")); myConn.SendNow(H.BuildResponse("404", "URL mismatch"));
H.Clean(); // clean for any possible next requests
return; return;
} }
userSelect.clear(); userSelect.clear();
@ -314,12 +307,10 @@ namespace Mist{
} }
if (M.getLive() && from < M.getFirstms(vidTrack)){ if (M.getLive() && from < M.getFirstms(vidTrack)){
H.Clean();
H.setCORSHeaders(); H.setCORSHeaders();
H.SetBody("The requested fragment is no longer kept in memory on the server and cannot be " H.SetBody("The requested fragment is no longer kept in memory on the server and cannot be "
"served.\n"); "served.\n");
myConn.SendNow(H.BuildResponse("404", "Fragment out of range")); myConn.SendNow(H.BuildResponse("404", "Fragment out of range"));
H.Clean(); // clean for any possible next requests
WARN_MSG("Fragment @ %" PRIu64 " too old", from); WARN_MSG("Fragment @ %" PRIu64 " too old", from);
return; return;
} }
@ -335,7 +326,6 @@ namespace Mist{
} }
if (method == "OPTIONS" || method == "HEAD"){ if (method == "OPTIONS" || method == "HEAD"){
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean();
return; return;
} }
@ -353,17 +343,14 @@ namespace Mist{
}else{ }else{
initialize(); initialize();
std::string request = H.url.substr(H.url.find("/", 5) + 1); std::string request = H.url.substr(H.url.find("/", 5) + 1);
H.Clean();
H.setCORSHeaders(); H.setCORSHeaders();
H.SetHeader("Content-Type", "application/vnd.apple.mpegurl"); H.SetHeader("Content-Type", "application/vnd.apple.mpegurl");
if (!M.getValidTracks().size()){ if (!M.getValidTracks().size()){
H.SendResponse("404", "Not online or found", myConn); H.SendResponse("404", "Not online or found", myConn);
H.Clean();
return; return;
} }
if (method == "OPTIONS" || method == "HEAD"){ if (method == "OPTIONS" || method == "HEAD"){
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean();
return; return;
} }
std::string manifest; std::string manifest;
@ -373,7 +360,6 @@ namespace Mist{
size_t idx = atoi(request.substr(0, request.find("/")).c_str()); size_t idx = atoi(request.substr(0, request.find("/")).c_str());
if (!M.getValidTracks().count(idx)){ if (!M.getValidTracks().count(idx)){
H.SendResponse("404", "No corresponding track found", myConn); H.SendResponse("404", "No corresponding track found", myConn);
H.Clean();
return; return;
} }
@ -389,7 +375,6 @@ namespace Mist{
if (thisPacket.getTime() >= until){ if (thisPacket.getTime() >= until){
stop(); stop();
wantRequest = true; wantRequest = true;
parseData = false;
// Ensure alignment of contCounters, to prevent discontinuities. // Ensure alignment of contCounters, to prevent discontinuities.
for (std::map<size_t, uint16_t>::iterator it = contCounters.begin(); it != contCounters.end(); it++){ for (std::map<size_t, uint16_t>::iterator it = contCounters.begin(); it != contCounters.end(); it++){
@ -407,6 +392,7 @@ namespace Mist{
// Signal end of data // Signal end of data
H.Chunkify("", 0, myConn); H.Chunkify("", 0, myConn);
H.Clean();
return; return;
} }
// Invoke the generic TS output sendNext handler // Invoke the generic TS output sendNext handler

View file

@ -14,7 +14,6 @@ namespace Mist{
idleInterval = 0; idleInterval = 0;
idleLast = 0; idleLast = 0;
if (config->getString("ip").size()){myConn.setHost(config->getString("ip"));} if (config->getString("ip").size()){myConn.setHost(config->getString("ip"));}
firstRun = true;
if (config->getString("prequest").size()){ if (config->getString("prequest").size()){
myConn.Received().prepend(config->getString("prequest")); myConn.Received().prepend(config->getString("prequest"));
} }
@ -197,14 +196,11 @@ namespace Mist{
if (!isBlocking && !parseData){Util::sleep(100);} if (!isBlocking && !parseData){Util::sleep(100);}
return; return;
} }
// If we can't read anything more and we're non-blocking, sleep some.
if (!firstRun && !myConn.spool()){
if (!isBlocking && !parseData){Util::sleep(100);}
return;
}
firstRun = false;
//Attempt to read a HTTP request, regardless of data being available
bool sawRequest = false;
while (H.Read(myConn)){ while (H.Read(myConn)){
sawRequest = true;
std::string handler = getHandler(); std::string handler = getHandler();
if (handler != capa["name"].asStringRef() || H.GetVar("stream") != streamName){ if (handler != capa["name"].asStringRef() || H.GetVar("stream") != streamName){
INFO_MSG("Received request: %s => %s (%s)", H.getUrl().c_str(), handler.c_str(), H.GetVar("stream").c_str()); INFO_MSG("Received request: %s => %s (%s)", H.getUrl().c_str(), handler.c_str(), H.GetVar("stream").c_str());
@ -307,8 +303,12 @@ namespace Mist{
if (!myConn){return;} if (!myConn){return;}
onHTTP(); onHTTP();
idleLast = Util::bootMS(); idleLast = Util::bootMS();
if (!H.bufferChunks){H.Clean();} // Prevent the clean as well as the loop when we're in the middle of handling a request now
if (!wantRequest){return;}
H.Clean();
} }
// If we can't read anything more and we're non-blocking, sleep some.
if (!sawRequest && !myConn.spool() && !isBlocking && !parseData){Util::sleep(100);}
} }
/// Default HTTP handler. /// Default HTTP handler.

View file

@ -27,7 +27,6 @@ namespace Mist{
bool parseRange(std::string header, uint64_t &byteStart, uint64_t &byteEnd); bool parseRange(std::string header, uint64_t &byteStart, uint64_t &byteEnd);
protected: protected:
bool firstRun;
bool responded; bool responded;
HTTP::Parser H; HTTP::Parser H;
HTTP::Websocket *webSock; HTTP::Websocket *webSock;