123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- #include "HTTPReader.h"
- #include "..\Components\wac_network\wac_network_http_receiver_api.h"
- #include "api__filereader.h"
- #include "../nu/AutoChar.h"
- #include <api/service/waservicefactory.h>
- #include <api/filereader/api_readercallback.h>
- #include <wchar.h>
- #include <bfc/platform/strcmp.h>
- #include <bfc/platform/minmax.h>
- #ifdef _WIN32
- #include <shlwapi.h>
- #endif
- #ifdef __APPLE__
- #include <unistd.h>
- #endif
- #include <stdexcept>
- // so we don't accidently call these CRT functions
- #ifdef close
- #undef close
- #endif
- #ifdef open
- #undef open
- #endif
- #ifdef read
- #undef read
- #endif
- #define config_guess_prebuffer true
- #define config_buffer_size 64
- #define config_prebuffer_size 24
- #define config_prebuffer_min 0
- #define config_allowseek true
- #define config_fullseek true
- #define config_seekprebuffer 1
- #define config_suppressstatus false
- // {C0A565DC-0CFE-405a-A27C-468B0C8A3A5C}
- static const GUID internetConfigGroupGUID =
- {
- 0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c }
- };
- class HttpReader
- {
- public:
- HttpReader(const char *url, uint64_t start_offset = 0, uint64_t total_len = 0, int is_seek = 0);
- ~HttpReader();
- int connect();
- int read(int8_t *buffer, int length);
-
- void abort() { killswitch = 1; }
-
- int bytesAvailable();
- uint64_t getContentLength()
- {
- if (m_contentlength)
- return m_contentlength;
- return -1;
- }
- int canSeek()
- {
- return (m_contentlength &&
- /* JF> this is correct but not as compatible: m_accept_ranges && */
- !m_meta_interval);
- }
- uint64_t getPos() { return m_contentpos; }
- const char *getHeader( const char *header ) { return httpGetter->getheader( (char *)header ); }
- void setMetaCB( api_readercallback *cb ) { metacb = cb; }
- //static BOOL CALLBACK httpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
- private:
- api_httpreceiver *httpGetter = NULL;
- api_dns *dns = NULL;
- char *m_AllHeaders;
- int buffer_size;
- int prebuffer_size, prebuffer_min;
- int need_prebuffer;
- uint64_t m_contentlength, m_contentpos;
- int m_accept_ranges;
- int proxy_enabled;
- char *proxy;
- int killswitch = -1;
- int m_meta_init, m_meta_interval, m_meta_pos, m_meta_size, m_meta_buf_pos;
- char m_meta_buf[4096];
- api_readercallback *metacb;
- int guessed_prebuffer_size;
- char lpinfo[256];
- char force_lpinfo[256];
- char *dlg_realm;
- char *m_url;
- };
- HttpReader::HttpReader( const char *url, uint64_t start_offset, uint64_t total_len, int is_seek )
- {
- m_accept_ranges = 0;
- buffer_size = (config_buffer_size * 1024);
- prebuffer_size = (config_prebuffer_size * 1024);
- prebuffer_min = (config_prebuffer_min * 1024);
- guessed_prebuffer_size = !config_guess_prebuffer;
- if (is_seek)
- {
- prebuffer_min = prebuffer_size = config_seekprebuffer;
- guessed_prebuffer_size = 1;
- }
- proxy_enabled = 0;
- killswitch = 0;
- need_prebuffer = 0;
- m_contentlength = total_len;
- m_contentpos = start_offset;
- m_meta_init = m_meta_interval = m_meta_pos = m_meta_size = m_meta_buf_pos = 0;
- m_meta_buf[0] = 0;
- metacb = NULL;
- force_lpinfo[0] = 0;
- lpinfo[0] = 0;
- m_url = _strdup(url);
- int use_proxy = 1;
- bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
- if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
- use_proxy = 0;
- waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid( httpreceiverGUID );
- if (sf)
- httpGetter = (api_httpreceiver *)sf->getInterface();
- const wchar_t *proxy = AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0);
- httpGetter->open(API_DNS_AUTODNS, buffer_size, (use_proxy && proxy && proxy[0]) ? (char *)AutoChar(proxy) : NULL);
- httpGetter->addheader("Accept:*/*");
- if (!_strnicmp(url, "uvox://", 7))
- {
- httpGetter->addheader("User-Agent: ultravox/2.0");
- }
- else
- {
- httpGetter->AddHeaderValue("User-Agent", AutoChar(WASABI_API_APP->main_getVersionString()));
- }
- if (start_offset > 0)
- {
- char temp[128];
- sprintf(temp, "Range: bytes=%d-", (int)start_offset);
- httpGetter->addheader(temp);
- }
- else
- httpGetter->addheader("Icy-Metadata:1");
- httpGetter->connect((char *)m_url, start_offset > 0);
- HttpReader::connect();
- HttpReader::read(0, 0);
- //if (!config_suppressstatus) api->core_setCustomMsg(0, StringPrintf("[Connecting] %s",url));
- }
- HttpReader::~HttpReader()
- {
- waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid( httpreceiverGUID );
- if ( sf )
- sf->releaseInterface( httpGetter );
- }
- // TODO: BOOL CALLBACK HttpReader::httpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
- int HttpReader::connect()
- {
- try
- {
- while ( killswitch >= 0 && httpGetter->run() == 0 && httpGetter->get_status() == 0 )
- {
- #ifdef _WIN32
- //Sleep( 50 );
- #else
- usleep( 50000 );
- #endif
- }
- if ( killswitch )
- return 0;
- if ( httpGetter->get_status() == -1 )
- {
- int code = httpGetter->getreplycode();
- if ( code == 401 )
- {
- /* TODO:
- // authorization required
- JNL_Connection *m_connection=httpGetter->get_con();
- char str[4096];
- while (m_connection->recv_lines_available() > 0) {
- char *wwwa="WWW-Authenticate:";
- m_connection->recv_line(str,4096);
- if (!str[0]) break;
- if (!_strnicmp(str,wwwa,strlen(wwwa))) {
- char *s2="Basic realm=\"";
- char *p=str+strlen(wwwa); while (p && *p== ' ') p++;
- if (!_strnicmp(p,s2,strlen(s2))) {
- p+=strlen(s2);
- if (strstr(p,"\"")) {
- strstr(p,"\"")[0]=0;
- if (*p) {
- if(force_lpinfo[0]) {
- force_lpinfo[0]=0; // invalid l/p
- } else WASABI_API_CONFIG->getStringPrivate(StringPrintf("HTTP-AUTH/%s",p),force_lpinfo,sizeof(force_lpinfo),"");
- if (!force_lpinfo[0] || lpinfo[0]) {
- dlg_realm = p;
- api->pushModalWnd();
- RootWnd *pr=api->main_getRootWnd();
- while(pr->getParent()) pr=pr->getParent();
- if (!DialogBoxParam(the->gethInstance(),MAKEINTRESOURCE(IDD_HTTPAUTH),pr->gethWnd(),httpDlgProc,(long)this)) {
- force_lpinfo[0]=0;
- } else {
- WASABI_API_CONFIG->setStringPrivate(StringPrintf("HTTP-AUTH/%s",p),force_lpinfo);
- }
- api->popModalWnd();
- }
- if (force_lpinfo[0]) {
- const char *p=STRSTR(m_url,"http://");
- if(p) {
- p+=7;
- StringPrintf tmp("http://%s@%s",force_lpinfo,p);
- httpGetter->connect((char *)tmp.getValue());
- return connect(); // recursive city
- }
- }
- }
- }
- }
- break;
- }
- }*/
- }
- // TODO: api->core_setCustomMsg(0, StringPrintf("HTTP: can't connect (%i)",code));
- return 0;
- }
- if ( httpGetter->getreplycode() < 200 || httpGetter->getreplycode() > 299 )
- {
- // TODO: api->core_setCustomMsg(0, StringPrintf("HTTP: returned %i",httpGetter->getreplycode()));
- return 0;
- }
- need_prebuffer = 1;
- }
- catch ( const std::exception &e )
- {
- return 0;
- }
- return 1;
- }
- int HttpReader::bytesAvailable()
- {
- int code = httpGetter->run();
- int ba = httpGetter->bytes_available();
- if ( !ba && code )
- return -1;
- return ba;
- }
- int HttpReader::read(int8_t *buffer, int length)
- {
- if (!httpGetter->GetConnection())
- return 0;
- if ( httpGetter->GetConnection()->get_state() == CONNECTION_STATE_CONNECTED && httpGetter->bytes_available() < prebuffer_min )
- need_prebuffer = 1;
- if (need_prebuffer)
- {
- need_prebuffer = 0;
- // TODO: if (!config_suppressstatus) api->core_setCustomMsg(0, "Prebuffering ...");
- if (!guessed_prebuffer_size)
- {
- // wait for headers
- int s;
- do
- {
- s = httpGetter->run();
- }
- while (s == 0 && httpGetter->get_status() != 2);
- // calculate the needed prebuffer size if it's a shoutcast stream
- const char *icybr;
- if (icybr = httpGetter->getheader("icy-br"))
- {
- prebuffer_size = (atoi(icybr) / 8) * 4096;
- prebuffer_min = (atoi(icybr) / 8) * 1024;
- if (prebuffer_size > buffer_size)
- prebuffer_size = buffer_size;
- }
- guessed_prebuffer_size = 1;
- }
- int last_pre = -1;
- while (httpGetter->bytes_available() < prebuffer_size && !killswitch)
- {
- int s = httpGetter->run();
- // JNL_Connection::state s = getter->get_state();
- // if (s == JNL_Connection::STATE_ERROR || s == JNL_Connection::STATE_CLOSED) break;
- if (s == -1 || s == 1) break;
- #ifdef _WIN32
- Sleep(50);
- #else
- usleep(50000);
- #endif
- if (last_pre != httpGetter->bytes_available() && !killswitch)
- {
- // TODO: if (!config_suppressstatus) api->core_setCustomMsg(0, StringPrintf(0, "Prebuffering : %i/%i bytes",httpGetter->bytes_available(),prebuffer_size));
- }
- }
- // if (!killswitch)
- // {
- //// TODO: if (!config_suppressstatus) api->core_setCustomMsg(0, "Prebuffering done.");
- // }
- }
- if (killswitch) return 0;
- // metadata filtering
- if ( !m_meta_init )
- {
- const char *v;
- if ( v = httpGetter->getheader( "icy-metaint:" ) )
- m_meta_interval = atoi( v );
- if ( !m_contentlength )
- {
- if ( v = httpGetter->getheader( "content-length:" ) )
- m_contentlength = _strtoui64( v, NULL, 10 );//atoi(v);
- }
- v = httpGetter->getheader( "accept-ranges:" );
- if ( v && strcasestr( v, "bytes" ) )
- m_accept_ranges = 1;
- m_meta_init = 1;
- }
- int error = 0, recvBytes = 0;
- while (length && !error && !killswitch)
- {
- int code = httpGetter->run();
- if (code != 0)
- error = 1;
- // old metadata parsing
- /*if (httpGetter->bytes_available()>0) {
- int l=httpGetter->get_bytes(buffer,length);
- // metadata stuff
- if (m_meta_interval) {
- int x=l;
- unsigned char *buf=(unsigned char *)buffer;
- if (m_meta_size)// already in meta block
- {
- int len=MIN(x,m_meta_size-m_meta_buf_pos);
- MEMCPY(m_meta_buf+m_meta_buf_pos,buf,len);
- m_meta_buf_pos+=len;
- if (m_meta_buf_pos==m_meta_size)
- {
- if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size);
- m_meta_buf_pos=0;
- m_meta_size=0;
- m_meta_pos=0;
- }
- x-=len;
- if (x) MEMCPY(buf,buf+len,x);
- }
- else if (m_meta_pos+x > m_meta_interval) // block contains meta data somewhere in it, and we're not alreayd reading a block
- {
- int start_offs=m_meta_interval-m_meta_pos;
- int len;
- m_meta_size=((unsigned char *)buf)[start_offs]*16;
- len=MIN(x-start_offs-1,m_meta_size);
- if (len) MEMCPY(m_meta_buf,buf+start_offs+1,len);
- m_meta_buf_pos=len;
- if (m_meta_buf_pos==m_meta_size) // full read of metadata successful
- {
- x-=m_meta_size+1;
- if (x > start_offs) MEMCPY(buf+start_offs,buf+start_offs+1+m_meta_size,x-start_offs);
- if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size);
- m_meta_buf_pos=0;
- m_meta_pos=-start_offs;
- m_meta_size=0;
- }
- else
- {
- x=start_offs; // otherwise, there's only the first block of data
- }
- }
- if (x > 0)
- {
- m_meta_pos+=x;
- }
- l=x;
- }
- length-=l;
- buffer+=l;
- recvBytes+=l;
- } else Sleep(50);*/
- while (1)
- {
- int len = httpGetter->bytes_available();
- if (m_meta_interval && m_meta_pos >= m_meta_interval)
- {
- unsigned char b;
- if (len > 0 && httpGetter->peek_bytes((char*)&b, 1) && len > (b << 4))
- {
- char metabuf[4096];
- httpGetter->get_bytes(metabuf, 1);
- httpGetter->get_bytes(metabuf, b << 4);
- if (metacb) metacb->metaDataReader_onData(metabuf, b << 4);
- //stream_metabytes_read+=(b<<4)+1;
- m_meta_pos = 0;
- }
- else
- break;
- }
- else
- {
- len = MIN(length, len);
- if (m_meta_interval)
- len = MIN(m_meta_interval - m_meta_pos, len);
- if (len > 0)
- {
- len = httpGetter->get_bytes((char*)buffer, len);
- m_meta_pos += len;
- //stream_bytes_read+=len;
- length -= len;
- buffer += len;
- recvBytes += len;
- }
- else
- {
- #ifdef _WIN32
- Sleep(50);
- #else
- usleep(50000);
- #endif
- }
- break;
- }
- }
- /* int s=httpGetter->get_con()->get_state();
- if(code==0) {*/
- /* char tmp[512];
- wsprintf(tmp,"[Connected] Retrieving list (%i bytes)", recvBytes);
- api->status_setText(tmp);*/
- // } else error=1;
- }
- m_contentpos += recvBytes;
-
- return recvBytes;
- }
- /* ---------------------------------------------------------------------- */
- int HTTPReader::isMine(const wchar_t *filename, int mode)
- {
- if (!_wcsnicmp(filename, L"http://", 7) ||
- !_wcsnicmp(filename, L"https://", 8) ||
- !_wcsnicmp(filename, L"icy://", 6) ||
- !_wcsnicmp(filename, L"sc://", 5) ||
- !_wcsnicmp(filename, L"uvox://", 7)) return 1;
- return 0;
- }
- int HTTPReader::open( const wchar_t *filename, int mode )
- {
- if ( !isMine( filename, mode ) )
- return 0;
- m_filename = _strdup( AutoChar( filename ) );
- reader = new HttpReader( m_filename );
- return 1;
- }
- uint64_t HTTPReader::bytesAvailable( uint64_t requested )
- {
- int v = reader ? reader->bytesAvailable() : 0;
- if ( v > requested )
- return requested;
- return v;
- }
- size_t HTTPReader::read( int8_t *buffer, size_t length )
- {
- if ( !reader )
- return 0;
- if ( !hasConnected )
- {
- int res = reader->connect();
- if ( !res )
- return 0;
- hasConnected = 1;
- }
- return reader->read( buffer, (int)length );
- }
- void HTTPReader::close()
- {
- delete reader;
- reader = NULL;
- }
- void HTTPReader::abort()
- {
- if ( reader )
- reader->abort();
- }
- uint64_t HTTPReader::getLength()
- {
- return reader ? reader->getContentLength() : -1;
- }
- uint64_t HTTPReader::getPos()
- {
- return reader ? reader->getPos() : 0;
- }
- int HTTPReader::canSeek()
- {
- return ( config_allowseek && reader && reader->canSeek() ) ? ( config_fullseek ? 1 : -1 ) : 0;
- }
- int HTTPReader::seek( uint64_t position )
- {
- if ( reader && reader->canSeek() && config_allowseek )
- {
- if ( position == getPos() ) return 0;
- hasConnected = 0;
- uint64_t cl = reader->getContentLength();
- delete( (HttpReader *)reader );
- reader = new HttpReader( m_filename, position, cl, 1 );
- return 0;
- }
- return -1;
- }
- int HTTPReader::hasHeaders()
- {
- return 1;
- }
- const char *HTTPReader::getHeader( const char *header )
- {
- return reader ? reader->getHeader( header ) : NULL;
- }
- void HTTPReader::setMetaDataCallback( api_readercallback *cb )
- {
- if ( reader )
- reader->setMetaCB( cb );
- }
- #define CBCLASS HTTPReader
- START_DISPATCH;
- CB(ISMINE, isMine);
- CB(OPEN, open);
- CB(READ, read);
- CB(WRITE, write);
- VCB(CLOSE, close);
- VCB(ABORT, abort);
- CB(GETLENGTH, getLength);
- CB(GETPOS, getPos);
- CB(CANSEEK, canSeek);
- CB(SEEK, seek);
- CB(HASHEADERS, hasHeaders);
- CB(GETHEADER, getHeader);
- CB(EXISTS, exists);
- // CB(REMOVE,remove);
- // CB(REMOVEUNDOABLE,removeUndoable);
- // CB(MOVE,move);
- CB(BYTESAVAILABLE, bytesAvailable);
- VCB(SETMETADATACALLBACK, setMetaDataCallback);
- CB(CANPREFETCH, canPrefetch);
- // CB(CANSETEOF, canSetEOF);
- // CB(SETEOF, setEOF);
- END_DISPATCH;
- #undef CBCLASS
|