diff options
author | thlo <smarttv640@gmail.com> | 2013-04-04 21:05:33 +0200 |
---|---|---|
committer | thlo <t.lohmar@gmx.de> | 2013-04-04 21:05:33 +0200 |
commit | 4c2f508b616652c05288aef8c11cfe10c9502d24 (patch) | |
tree | d79396cb2cfb4bde345a4c8339d38098ff5b3eb0 /httpclient.c | |
parent | afb4d3bebf5e4ecbaba0cee23a66bd745e07ef4c (diff) | |
download | vdr-plugin-smarttvweb-4c2f508b616652c05288aef8c11cfe10c9502d24.tar.gz vdr-plugin-smarttvweb-4c2f508b616652c05288aef8c11cfe10c9502d24.tar.bz2 |
New urls manager for YouTube URLs. New http client class. Various bug fixes.
Diffstat (limited to 'httpclient.c')
-rw-r--r-- | httpclient.c | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/httpclient.c b/httpclient.c new file mode 100644 index 0000000..cdf7645 --- /dev/null +++ b/httpclient.c @@ -0,0 +1,361 @@ +/* + * httpclient.c: VDR on Smart TV plugin + * + * Copyright (C) 2012, 2013 T. Lohmar + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#include "httpclient.h" +#include "smarttvfactory.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <cstdlib> +#include <sstream> + +#define OKAY 0 +#define ERROR (-1) + + +#define DEBUG + +#define DEBUGPREFIX "mClient= " << mReqId << " fd= " << mFd + +cHttpClientBase::cHttpClientBase(int f, int id, int port, SmartTvServer* factory, string peer) : cHttpResourceBase(f, id, port, factory), + mLog(), mRequestMessage(""), mRequestMessagePos(0), mConnState(0), mResponseHdr(), mRespBdyLen(-1), + mStatus(-1), mIsChunked(false), mResponseBdy(), + mPeer(peer), mTransCount(0) { + + mLog = Log::getInstance(); + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << "cHttpClientBase Constructor" << endl; +#endif + mRequestMessage = ""; +} + +cHttpClientBase::~cHttpClientBase() { + *(mLog->log()) << "cHttpClientBase Destructor" << endl; +} + +int cHttpClientBase::handleRead() { + if ((mConnState == 0) || (mConnState ==3)) { + *(mLog->log()) << DEBUGPREFIX + << " Read during write or wait state. Ok, return." << endl; + return OKAY; + } +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " handleRead" << endl; +#endif + + const size_t bufflen = 4096; + ssize_t bytesreceived = 0; + char buff[bufflen]; + + string tmp = ""; + bytesreceived = recv(mFd, buff, bufflen, 0); + if (bytesreceived <0 ) { + *(mLog->log()) << DEBUGPREFIX << " ERROR while receiving" << endl; + return ERROR; + } + + if (bytesreceived ==0 ) { +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " Done: No bytes received anymore -> Closing" + << " mRespBdyLen= " << mRespBdyLen + << " mResponseBdy.size()= " << mResponseBdy.size() + << endl; +#endif + return ERROR; + } + + tmp = string(buff, bytesreceived); + + size_t pos ; + switch (mConnState) { + case 1: + // looking for header end + pos = tmp.find("\r\n\r\n"); + + if (pos == string::npos) + mResponseHdr += tmp; + else { + // Header End found +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " Header End Found. Setting mConnState to 2" + << endl; +#endif + + mResponseHdr += tmp.substr(0, pos); + if (pos+4 < tmp.size()) + mResponseBdy = tmp.substr(pos+4); + mConnState = 2; + processResponseHeader(); + } + + break; + case 2: + // end of state 2 is determined in checkTransactionCompletion + mResponseBdy += tmp; + break; + default: + *(mLog->log()) << DEBUGPREFIX + << " ERROR in handleRead: Def: " << tmp << endl; + break; + } + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " handleRead - end" << endl; +#endif + return checkTransactionCompletion(); +} + +int cHttpClientBase::handleWrite() { + if (mConnState == 3) { + // waiting + timeval now; + gettimeofday(&now, 0); + + long diff; // in ms + diff = (now.tv_sec - mWaitStart.tv_sec) *1000; + diff += (now.tv_usec - mWaitStart.tv_usec) /1000; + mWaitCount++; + + if ((diff > 40) || (mWaitCount>10000)) { + createRequestMessage(""); + } + return OKAY; + + } + if (mConnState >0) { + return OKAY; + } + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " ++++ handleWrite ++++" << endl; +#endif + + int rem_len = mRequestMessage.size() - mRequestMessagePos; + if (rem_len == 0) { + mConnState = 1; // Change state to Read + *(mLog->log()) << DEBUGPREFIX + << " WARNING: Should not be here " << endl; + return OKAY; + } + + string tmp = mRequestMessage.substr(mRequestMessagePos, rem_len); + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " handleWrite: mTransCount= " << mTransCount + << " Msg.size= " << mRequestMessage.size() + << endl; +#endif + int snd = send(mFd, tmp.c_str(), (size_t) tmp.size(), 0); + + if (snd <0) { + *(mLog->log()) << DEBUGPREFIX << " ERROR while sending" << endl; + return ERROR; + } + + if (snd == rem_len) { + // done with sending. Reading now + mConnState = 1; +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " -> Done with Sending. Reading now" << endl; +#endif + } + else { + *(mLog->log()) << DEBUGPREFIX << " Sent " << snd << " byte. Continue writing request." << endl; + } + mRequestMessagePos+= snd; + return OKAY; +} + +int cHttpClientBase::checkStatus() { + // TODO: Should check for stalled client + return OKAY; +} + +void cHttpClientBase::createRequestMessage(string path) { + mResponseHdr= ""; + mResponseBdy= ""; + mRespBdyLen = -1; + mRequestMessagePos = 0; + mStatus = -1; + mConnState = 0; // WriteRequest + + string req_body = ""; + stringstream cont_len; + + string device_id = "12331"; + string device_name = "VdrOnTv"; + string vendor = "mVdrOnTv"; + string product = "SMARTDev"; + + + switch (mTransCount) { + case 0: + mRequestMessage = "POST /ws/app/VdrOnTv/connect HTTP/1.1\r\n"; + mRequestMessage += "Host: " + mPeer + "\r\n"; + mRequestMessage += "SLDeviceID: "+device_id +"\r\n"; + mRequestMessage += "ProductID: "+product+"\r\n"; + mRequestMessage += "VendorID: "+vendor+"\r\n"; + mRequestMessage += "DeviceName: "+device_name+"\r\n"; + mRequestMessage += "Connection: keep-alive\r\n"; + mRequestMessage += "Content-Length: 0\r\n"; + mRequestMessage += "Accept: */*\r\n"; + mRequestMessage += "\r\n"; + + break; + case 1: +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " NextMessage: mTransCount= " << mTransCount + << endl; +#endif + + req_body = getMsgBody(mTransCount); + cont_len << "Content-Length: " << req_body.size() << "\r\n"; + + mRequestMessage = "POST /ws/app/VdrOnTv/queue HTTP/1.1\r\n"; + mRequestMessage += "Host: " + mPeer + "\r\n"; + mRequestMessage += "SLDeviceID: "+device_id +"\r\n"; + mRequestMessage += "Accept: */*\r\n"; + mRequestMessage += "Connection: keep-alive\r\n"; + mRequestMessage += "Content-Type: application/json\r\n"; + mRequestMessage += cont_len.str(); + + mRequestMessage += "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n"; + mRequestMessage += "\r\n"; + + mRequestMessage += req_body; + + break; + + default: + *(mLog->log()) << DEBUGPREFIX + << " ############ ERROR, mTransCount= " << mTransCount + << endl; + break; + + } +} + + +void cHttpClientBase::processResponseHeader() { + mRespBdyLen = -1; + + size_t s_sp = mResponseHdr.find(" "); + size_t s_sp2 = mResponseHdr.find(" ", s_sp+1); + + mStatus = atoi(mResponseHdr.substr(s_sp, (s_sp2-s_sp)).c_str()); + *(mLog->log()) << DEBUGPREFIX + << " mStatus= " << mStatus + << endl; + size_t c_start = mResponseHdr.find("Content-Length:"); + if (c_start != string::npos) { + size_t c_col = mResponseHdr.find(':', c_start); + size_t c_end = mResponseHdr.find('\r', c_start); + + mRespBdyLen = atoi(mResponseHdr.substr(c_col +1, (c_end -c_col -1)).c_str()); + *(mLog->log()) << DEBUGPREFIX + << " Content-Length(val)= " << mRespBdyLen + << endl; + + } // if (c_start != string::npos) { + else { + *(mLog->log()) << DEBUGPREFIX + << " Content-Length not found " + << endl; + } + + size_t tr_start = mResponseHdr.find("Transfer-Encoding:"); + if (tr_start != string::npos) { + size_t tr_col = mResponseHdr.find(':', tr_start); + size_t tr_end = mResponseHdr.find('\r', tr_start); + + string chunked = mResponseHdr.substr(tr_col +1, (tr_end -tr_col -1)); + *(mLog->log()) << DEBUGPREFIX + << " Transfer-Encoding(val)= " << chunked + << endl; + mIsChunked = false; + + } // if (tr_start != string::npos) { + +} + + +int cHttpClientBase::checkTransactionCompletion() { + // hmm, here I assume that the response is received in first byte chunk + // should first check that I am in state 2 (hdr received) + if (mConnState < 2) + return OKAY; + + //OK, header received + if (mStatus != 200) { + *(mLog->log()) << DEBUGPREFIX + << " Closing.... Status= " << mStatus + << endl; + return ERROR; + } + + if ((mRespBdyLen <= mResponseBdy.size()) || (mRespBdyLen == 0)) { + + *(mLog->log()) << DEBUGPREFIX + << " Transaction completed. mTransCount= " << mTransCount + << endl; + + if (mTransCount ==0 ) { + // ok + gettimeofday(&mWaitStart, 0); + mWaitCount = 0; + mConnState = 3; + mTransCount++; + return OKAY; + } + else + return ERROR; + } + return OKAY; +} + +//------------------------------ +//----- cHttpYtPushClient ------ +//------------------------------ + +cHttpYtPushClient::cHttpYtPushClient(int f, int id, int port, SmartTvServer* fac, string peer, string vid, bool store) : cHttpClientBase(f, id, port, fac, peer), + mVideoId(vid), mStore(store) { + + createRequestMessage(""); +} + +cHttpYtPushClient::~cHttpYtPushClient() { +} + +string cHttpYtPushClient::getMsgBody(int) { + return "{\"type\":\"YT\",payload:{\"id\":\"" + mVideoId +"\", \"store\":"+((mStore)?"true":"false")+"}}"; +} + |