1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466 |
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include "internal.h"
- #include <ctype.h>
- #include <errno.h>
- #include <limits.h>
- #include <string.h>
- typedef struct OpusParsedURL OpusParsedURL;
- typedef struct OpusStringBuf OpusStringBuf;
- typedef struct OpusHTTPConn OpusHTTPConn;
- typedef struct OpusHTTPStream OpusHTTPStream;
- static char *op_string_range_dup(const char *_start,const char *_end){
- size_t len;
- char *ret;
- OP_ASSERT(_start<=_end);
- len=_end-_start;
-
- if(OP_UNLIKELY(len>=INT_MAX))return NULL;
- ret=(char *)_ogg_malloc(sizeof(*ret)*(len+1));
- if(OP_LIKELY(ret!=NULL)){
- ret=(char *)memcpy(ret,_start,sizeof(*ret)*(len));
- ret[len]='\0';
- }
- return ret;
- }
- static char *op_string_dup(const char *_s){
- return op_string_range_dup(_s,_s+strlen(_s));
- }
- static char *op_string_tolower(char *_s){
- int i;
- for(i=0;_s[i]!='\0';i++){
- int c;
- c=_s[i];
- if(c>='A'&&c<='Z')c+='a'-'A';
- _s[i]=(char)c;
- }
- return _s;
- }
- #define OP_URL_ALPHA \
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- #define OP_URL_DIGIT "0123456789"
- #define OP_URL_HEXDIGIT "0123456789ABCDEFabcdef"
- #define OP_URL_SCHEME OP_URL_ALPHA OP_URL_DIGIT "+-."
- #define OP_URL_GEN_DELIMS "#/:?@[]"
- #define OP_URL_SUB_DELIMS "!$&'()*+,;="
- #define OP_URL_RESERVED OP_URL_GEN_DELIMS OP_URL_SUB_DELIMS
- #define OP_URL_UNRESERVED OP_URL_ALPHA OP_URL_DIGIT "-._~"
- #define OP_URL_PCT_ENCODED "%"
- #define OP_URL_PCHAR_BASE \
- OP_URL_UNRESERVED OP_URL_PCT_ENCODED OP_URL_SUB_DELIMS
- #define OP_URL_PCHAR OP_URL_PCHAR_BASE ":@"
- #define OP_URL_PCHAR_NA OP_URL_PCHAR_BASE ":"
- #define OP_URL_PCHAR_NC OP_URL_PCHAR_BASE "@"
- #define OP_URL_PATH OP_URL_PCHAR "/"
- #define OP_URL_QUERY_FRAG OP_URL_PCHAR "/?"
- static int op_validate_url_escapes(const char *_s){
- int i;
- for(i=0;_s[i];i++){
- if(_s[i]=='%'){
- if(OP_UNLIKELY(!isxdigit(_s[i+1]))
- ||OP_UNLIKELY(!isxdigit(_s[i+2]))
-
- ||OP_UNLIKELY(_s[i+1]=='0'&&_s[i+2]=='0')){
- return OP_FALSE;
- }
- i+=2;
- }
- }
- return 0;
- }
- static int op_hex_value(int _c){
- return _c>='a'?_c-'a'+10:_c>='A'?_c-'A'+10:_c-'0';
- }
- static char *op_unescape_url_component(char *_s){
- int i;
- int j;
- for(i=j=0;_s[i];i++,j++){
- if(_s[i]=='%'){
- _s[i]=(char)(op_hex_value(_s[i+1])<<4|op_hex_value(_s[i+2]));
- i+=2;
- }
- }
- return _s;
- }
- static const char *op_parse_file_url(const char *_src){
- const char *scheme_end;
- const char *path;
- const char *path_end;
- scheme_end=_src+strspn(_src,OP_URL_SCHEME);
- if(OP_UNLIKELY(*scheme_end!=':')
- ||scheme_end-_src!=4||op_strncasecmp(_src,"file",4)!=0){
-
- return NULL;
- }
-
- if(OP_UNLIKELY(op_validate_url_escapes(scheme_end+1)<0))return NULL;
- if(scheme_end[1]=='/'&&scheme_end[2]=='/'){
- const char *host;
-
- host=scheme_end+3;
-
- if(OP_LIKELY(*host=='/'))path=host;
- else{
- const char *host_end;
- char host_buf[28];
-
- host_end=host+strspn(host,OP_URL_PCHAR_BASE);
-
- if(*host_end!='/')return NULL;
-
- if(OP_UNLIKELY(host_end-host>27))return NULL;
- memcpy(host_buf,host,sizeof(*host_buf)*(host_end-host));
- host_buf[host_end-host]='\0';
- op_unescape_url_component(host_buf);
- op_string_tolower(host_buf);
-
- if(OP_UNLIKELY(strcmp(host_buf,"localhost")!=0))return NULL;
- path=host_end;
- }
- }
- else path=scheme_end+1;
- path_end=path+strspn(path,OP_URL_PATH);
-
- if(*path_end!='\0')return NULL;
- return path;
- }
- #if defined(OP_ENABLE_HTTP)
- # if defined(_WIN32)
- # include <winsock2.h>
- # include <ws2tcpip.h>
- # include <openssl/ssl.h>
- # include "winerrno.h"
- typedef SOCKET op_sock;
- # define OP_INVALID_SOCKET (INVALID_SOCKET)
- # if !defined(POLLIN)
- # define POLLRDNORM (0x0100)
- # define POLLRDBAND (0x0200)
- # define POLLIN (POLLRDNORM|POLLRDBAND)
- # define POLLPRI (0x0400)
- # define POLLWRNORM (0x0010)
- # define POLLOUT (POLLWRNORM)
- # define POLLWRBAND (0x0020)
- # define POLLERR (0x0001)
- # define POLLHUP (0x0002)
- # define POLLNVAL (0x0004)
- struct pollfd{
-
- op_sock fd;
-
- short events;
-
- short revents;
- };
- # endif
- typedef unsigned long nfds_t;
- static int op_poll_win32(struct pollfd *_fds,nfds_t _nfds,int _timeout){
- struct timeval tv;
- fd_set ifds;
- fd_set ofds;
- fd_set efds;
- nfds_t i;
- int ret;
- FD_ZERO(&ifds);
- FD_ZERO(&ofds);
- FD_ZERO(&efds);
- for(i=0;i<_nfds;i++){
- _fds[i].revents=0;
- if(_fds[i].events&POLLIN)FD_SET(_fds[i].fd,&ifds);
- if(_fds[i].events&POLLOUT)FD_SET(_fds[i].fd,&ofds);
- FD_SET(_fds[i].fd,&efds);
- }
- if(_timeout>=0){
- tv.tv_sec=_timeout/1000;
- tv.tv_usec=(_timeout%1000)*1000;
- }
- ret=select(-1,&ifds,&ofds,&efds,_timeout<0?NULL:&tv);
- if(ret>0){
- for(i=0;i<_nfds;i++){
- if(FD_ISSET(_fds[i].fd,&ifds))_fds[i].revents|=POLLIN;
- if(FD_ISSET(_fds[i].fd,&ofds))_fds[i].revents|=POLLOUT;
-
- if(FD_ISSET(_fds[i].fd,&efds))_fds[i].revents|=POLLHUP;
- }
- }
- return ret;
- }
- # define op_errno() (WSAGetLastError()?WSAGetLastError()-WSABASEERR:0)
- # define op_reset_errno() (WSASetLastError(0))
- # define close(_fd) closesocket(_fd)
- # define ioctl(_fd,_req,_arg) ioctlsocket(_fd,_req,(u_long *)(_arg))
- # define getsockopt(_fd,_level,_name,_val,_len) \
- getsockopt(_fd,_level,_name,(char *)(_val),_len)
- # define setsockopt(_fd,_level,_name,_val,_len) \
- setsockopt(_fd,_level,_name,(const char *)(_val),_len)
- # define poll(_fds,_nfds,_timeout) op_poll_win32(_fds,_nfds,_timeout)
- # if defined(_MSC_VER)
- typedef ptrdiff_t ssize_t;
- # endif
- int SSL_CTX_set_default_verify_paths_win32(SSL_CTX *_ssl_ctx);
- # define SSL_CTX_set_default_verify_paths \
- SSL_CTX_set_default_verify_paths_win32
- # else
- # include <sys/ioctl.h>
- # include <sys/types.h>
- # include <sys/socket.h>
- # include <arpa/inet.h>
- # include <netinet/in.h>
- # include <netinet/tcp.h>
- # include <fcntl.h>
- # include <netdb.h>
- # include <poll.h>
- # include <unistd.h>
- # include <openssl/ssl.h>
- typedef int op_sock;
- # define OP_INVALID_SOCKET (-1)
- # define op_errno() (errno)
- # define op_reset_errno() (errno=0)
- # endif
- # include <sys/timeb.h>
- # include <openssl/x509v3.h>
- # define OP_NCONNS_MAX (4)
- # define OP_RESOLVE_CACHE_TIMEOUT_MS (10*60*(opus_int32)1000)
- # define OP_REDIRECT_LIMIT (20)
- # define OP_RESPONSE_SIZE_MIN (510)
- # define OP_RESPONSE_SIZE_MAX (32766)
- # define OP_CONNECTION_IDLE_TIMEOUT_MS (5*1000)
- # define OP_POLL_TIMEOUT_MS (30*1000)
- # define OP_READAHEAD_THRESH_MIN (32*(opus_int32)1024)
- # define OP_PIPELINE_CHUNK_SIZE (32*(opus_int32)1024)
- # define OP_PIPELINE_CHUNK_SIZE_MAX (1024*(opus_int32)1024)
- # define OP_PIPELINE_MAX_REQUESTS (100)
- # define OP_PIPELINE_MIN_REQUESTS (7)
- # define OP_URL_IS_SSL(_url) ((_url)->scheme[4]=='s')
- # define OP_URL_IS_DEFAULT_PORT(_url) \
- (!OP_URL_IS_SSL(_url)&&(_url)->port==80 \
- ||OP_URL_IS_SSL(_url)&&(_url)->port==443)
- struct OpusParsedURL{
-
- char *scheme;
-
- char *user;
-
- char *pass;
-
- char *host;
-
- char *path;
-
- unsigned port;
- };
- static int op_parse_url_impl(OpusParsedURL *_dst,const char *_src){
- const char *scheme_end;
- const char *authority;
- const char *userinfo_end;
- const char *user;
- const char *user_end;
- const char *pass;
- const char *hostport;
- const char *hostport_end;
- const char *host_end;
- const char *port;
- opus_int32 port_num;
- const char *port_end;
- const char *path;
- const char *path_end;
- const char *uri_end;
- scheme_end=_src+strspn(_src,OP_URL_SCHEME);
- if(OP_UNLIKELY(*scheme_end!=':')
- ||OP_UNLIKELY(scheme_end-_src<4)||OP_UNLIKELY(scheme_end-_src>5)
- ||OP_UNLIKELY(op_strncasecmp(_src,"https",scheme_end-_src)!=0)){
-
- return OP_EIMPL;
- }
- if(OP_UNLIKELY(scheme_end[1]!='/')||OP_UNLIKELY(scheme_end[2]!='/')){
-
- return OP_EINVAL;
- }
- authority=scheme_end+3;
-
- if(OP_UNLIKELY(op_validate_url_escapes(authority)<0))return OP_EINVAL;
-
- userinfo_end=authority+strspn(authority,OP_URL_PCHAR_NA);
- if(*userinfo_end=='@'){
-
- user=authority;
-
- user_end=authority+strspn(authority,OP_URL_PCHAR_BASE);
- if(*user_end==':')pass=user_end+1;
- else pass=NULL;
- hostport=userinfo_end+1;
- }
- else{
-
- user=user_end=NULL;
- pass=NULL;
- hostport=authority;
- }
-
- if(hostport[0]=='['){
- hostport++;
-
- hostport_end=host_end=hostport+strspn(hostport,OP_URL_PCHAR_NA);
- if(OP_UNLIKELY(*hostport_end++!=']'))return OP_EINVAL;
- }
-
- else hostport_end=host_end=hostport+strspn(hostport,OP_URL_PCHAR_BASE);
-
-
- port_num=-1;
- if(*hostport_end==':'){
- int i;
- port=hostport_end+1;
- port_end=port+strspn(port,OP_URL_DIGIT);
- path=port_end;
-
- if(OP_LIKELY(port_end-port>0)){
- while(*port=='0')port++;
- if(OP_UNLIKELY(port_end-port>5))return OP_EINVAL;
- port_num=0;
- for(i=0;i<port_end-port;i++)port_num=port_num*10+port[i]-'0';
- if(OP_UNLIKELY(port_num>65535))return OP_EINVAL;
- }
- }
- else path=hostport_end;
- path_end=path+strspn(path,OP_URL_PATH);
-
- if(OP_LIKELY(path_end>path)&&OP_UNLIKELY(path[0]!='/'))return OP_EINVAL;
-
- if(*path_end=='?')path_end=path_end+strspn(path_end,OP_URL_QUERY_FRAG);
-
- if(*path_end=='#')uri_end=path_end+1+strspn(path_end+1,OP_URL_QUERY_FRAG);
- else uri_end=path_end;
-
- if(OP_UNLIKELY(*uri_end!='\0'))return OP_EINVAL;
- _dst->scheme=op_string_range_dup(_src,scheme_end);
- if(OP_UNLIKELY(_dst->scheme==NULL))return OP_EFAULT;
- op_string_tolower(_dst->scheme);
- if(user!=NULL){
- _dst->user=op_string_range_dup(user,user_end);
- if(OP_UNLIKELY(_dst->user==NULL))return OP_EFAULT;
- op_unescape_url_component(_dst->user);
-
- if(OP_UNLIKELY(strchr(_dst->user,':')!=NULL))return OP_EINVAL;
- }
- else _dst->user=NULL;
- if(pass!=NULL){
- _dst->pass=op_string_range_dup(pass,userinfo_end);
- if(OP_UNLIKELY(_dst->pass==NULL))return OP_EFAULT;
- op_unescape_url_component(_dst->pass);
- }
- else _dst->pass=NULL;
- _dst->host=op_string_range_dup(hostport,host_end);
- if(OP_UNLIKELY(_dst->host==NULL))return OP_EFAULT;
- if(port_num<0){
- if(_src[4]=='s')port_num=443;
- else port_num=80;
- }
- _dst->port=(unsigned)port_num;
-
- if(path==path_end||path[0]=='?')path--;
- _dst->path=op_string_range_dup(path,path_end);
- if(OP_UNLIKELY(_dst->path==NULL))return OP_EFAULT;
-
- _dst->path[0]='/';
- return 0;
- }
- static void op_parsed_url_init(OpusParsedURL *_url){
- memset(_url,0,sizeof(*_url));
- }
- static void op_parsed_url_clear(OpusParsedURL *_url){
- _ogg_free(_url->scheme);
- _ogg_free(_url->user);
- _ogg_free(_url->pass);
- _ogg_free(_url->host);
- _ogg_free(_url->path);
- }
- static int op_parse_url(OpusParsedURL *_dst,const char *_src){
- OpusParsedURL url;
- int ret;
- op_parsed_url_init(&url);
- ret=op_parse_url_impl(&url,_src);
- if(OP_UNLIKELY(ret<0))op_parsed_url_clear(&url);
- else *_dst=*&url;
- return ret;
- }
- struct OpusStringBuf{
- char *buf;
- int nbuf;
- int cbuf;
- };
- static void op_sb_init(OpusStringBuf *_sb){
- _sb->buf=NULL;
- _sb->nbuf=0;
- _sb->cbuf=0;
- }
- static void op_sb_clear(OpusStringBuf *_sb){
- _ogg_free(_sb->buf);
- }
- static int op_sb_ensure_capacity(OpusStringBuf *_sb,int _capacity){
- char *buf;
- int cbuf;
- buf=_sb->buf;
- cbuf=_sb->cbuf;
- if(_capacity>=cbuf-1){
- if(OP_UNLIKELY(cbuf>INT_MAX-1>>1))return OP_EFAULT;
- if(OP_UNLIKELY(_capacity>=INT_MAX-1))return OP_EFAULT;
- cbuf=OP_MAX(2*cbuf+1,_capacity+1);
- buf=_ogg_realloc(buf,sizeof(*buf)*cbuf);
- if(OP_UNLIKELY(buf==NULL))return OP_EFAULT;
- _sb->buf=buf;
- _sb->cbuf=cbuf;
- }
- return 0;
- }
- static int op_sb_grow(OpusStringBuf *_sb,int _max_size){
- char *buf;
- int cbuf;
- buf=_sb->buf;
- cbuf=_sb->cbuf;
- OP_ASSERT(_max_size<=INT_MAX-1);
- cbuf=cbuf<=_max_size-1>>1?2*cbuf+1:_max_size+1;
- buf=_ogg_realloc(buf,sizeof(*buf)*cbuf);
- if(OP_UNLIKELY(buf==NULL))return OP_EFAULT;
- _sb->buf=buf;
- _sb->cbuf=cbuf;
- return 0;
- }
- static int op_sb_append(OpusStringBuf *_sb,const char *_s,int _len){
- char *buf;
- int nbuf;
- int ret;
- nbuf=_sb->nbuf;
- if(OP_UNLIKELY(nbuf>INT_MAX-_len))return OP_EFAULT;
- ret=op_sb_ensure_capacity(_sb,nbuf+_len);
- if(OP_UNLIKELY(ret<0))return ret;
- buf=_sb->buf;
- memcpy(buf+nbuf,_s,sizeof(*buf)*_len);
- nbuf+=_len;
- buf[nbuf]='\0';
- _sb->nbuf=nbuf;
- return 0;
- }
- static int op_sb_append_string(OpusStringBuf *_sb,const char *_s){
- return op_sb_append(_sb,_s,strlen(_s));
- }
- static int op_sb_append_port(OpusStringBuf *_sb,unsigned _port){
- char port_buf[7];
- OP_ASSERT(_port<=65535U);
- sprintf(port_buf,":%u",_port);
- return op_sb_append_string(_sb,port_buf);
- }
- static int op_sb_append_nonnegative_int64(OpusStringBuf *_sb,opus_int64 _i){
- char digit;
- int nbuf_start;
- int ret;
- OP_ASSERT(_i>=0);
- nbuf_start=_sb->nbuf;
- ret=0;
- do{
- digit='0'+_i%10;
- ret|=op_sb_append(_sb,&digit,1);
- _i/=10;
- }
- while(_i>0);
- if(OP_LIKELY(ret>=0)){
- char *buf;
- int nbuf_end;
- buf=_sb->buf;
- nbuf_end=_sb->nbuf-1;
-
- while(nbuf_start<nbuf_end){
- digit=buf[nbuf_start];
- buf[nbuf_start]=buf[nbuf_end];
- buf[nbuf_end]=digit;
- nbuf_start++;
- nbuf_end--;
- }
- }
- return ret;
- }
- static struct addrinfo *op_resolve(const char *_host,unsigned _port){
- struct addrinfo *addrs;
- struct addrinfo hints;
- char service[6];
- memset(&hints,0,sizeof(hints));
- hints.ai_socktype=SOCK_STREAM;
- #if defined(AI_NUMERICSERV)
- hints.ai_flags=AI_NUMERICSERV;
- #endif
- OP_ASSERT(_port<=65535U);
- sprintf(service,"%u",_port);
- if(OP_LIKELY(!getaddrinfo(_host,service,&hints,&addrs)))return addrs;
- return NULL;
- }
- static int op_sock_set_nonblocking(op_sock _fd,int _nonblocking){
- #if !defined(_WIN32)
- int flags;
- flags=fcntl(_fd,F_GETFL);
- if(OP_UNLIKELY(flags<0))return flags;
- if(_nonblocking)flags|=O_NONBLOCK;
- else flags&=~O_NONBLOCK;
- return fcntl(_fd,F_SETFL,flags);
- #else
- return ioctl(_fd,FIONBIO,&_nonblocking);
- #endif
- }
- static void op_sock_set_tcp_nodelay(op_sock _fd,int _nodelay){
- # if defined(TCP_NODELAY)&&(defined(IPPROTO_TCP)||defined(SOL_TCP))
- # if defined(IPPROTO_TCP)
- # define OP_SO_LEVEL IPPROTO_TCP
- # else
- # define OP_SO_LEVEL SOL_TCP
- # endif
-
- OP_ALWAYS_TRUE(!setsockopt(_fd,OP_SO_LEVEL,TCP_NODELAY,
- &_nodelay,sizeof(_nodelay)));
- # endif
- }
- #if defined(_WIN32)
- static void op_init_winsock(){
- static LONG count;
- static WSADATA wsadata;
- if(InterlockedIncrement(&count)==1)WSAStartup(0x0202,&wsadata);
- }
- #endif
- struct OpusHTTPConn{
-
- opus_int64 pos;
-
- opus_int64 end_pos;
-
- opus_int64 next_pos;
-
- opus_int64 next_end;
-
- SSL *ssl_conn;
-
- OpusHTTPConn *next;
-
- struct timeb read_time;
-
- opus_int64 read_bytes;
-
- opus_int64 read_rate;
-
- op_sock fd;
-
- int nrequests_left;
-
- opus_int32 chunk_size;
- };
- static void op_http_conn_init(OpusHTTPConn *_conn){
- _conn->next_pos=-1;
- _conn->ssl_conn=NULL;
- _conn->next=NULL;
- _conn->fd=OP_INVALID_SOCKET;
- }
- static void op_http_conn_clear(OpusHTTPConn *_conn){
- if(_conn->ssl_conn!=NULL)SSL_free(_conn->ssl_conn);
-
- if(_conn->fd!=OP_INVALID_SOCKET)close(_conn->fd);
- }
- struct OpusHTTPStream{
-
- OpusHTTPConn conns[OP_NCONNS_MAX];
-
- SSL_CTX *ssl_ctx;
-
- SSL_SESSION *ssl_session;
-
- OpusHTTPConn *lru_head;
-
- OpusHTTPConn *free_head;
-
- OpusParsedURL url;
-
- struct addrinfo addr_info;
-
- union{
- struct sockaddr s;
- struct sockaddr_in v4;
- struct sockaddr_in6 v6;
- } addr;
-
- struct timeb resolve_time;
-
- OpusStringBuf request;
-
- OpusStringBuf proxy_connect;
-
- OpusStringBuf response;
-
- opus_int64 content_length;
-
- opus_int64 pos;
-
- char *connect_host;
-
- unsigned connect_port;
-
- int cur_conni;
-
- int seekable;
-
- int pipeline;
-
- int skip_certificate_check;
-
- int request_tail;
-
- opus_int32 connect_rate;
- };
- static void op_http_stream_init(OpusHTTPStream *_stream){
- OpusHTTPConn **pnext;
- int ci;
- pnext=&_stream->free_head;
- for(ci=0;ci<OP_NCONNS_MAX;ci++){
- op_http_conn_init(_stream->conns+ci);
- *pnext=_stream->conns+ci;
- pnext=&_stream->conns[ci].next;
- }
- _stream->ssl_ctx=NULL;
- _stream->ssl_session=NULL;
- _stream->lru_head=NULL;
- op_parsed_url_init(&_stream->url);
- op_sb_init(&_stream->request);
- op_sb_init(&_stream->proxy_connect);
- op_sb_init(&_stream->response);
- _stream->connect_host=NULL;
- _stream->seekable=0;
- }
- static void op_http_conn_close(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
- OpusHTTPConn **_pnext,int _gracefully){
-
- if(_gracefully&&_conn->ssl_conn!=NULL)SSL_shutdown(_conn->ssl_conn);
- op_http_conn_clear(_conn);
- _conn->next_pos=-1;
- _conn->ssl_conn=NULL;
- _conn->fd=OP_INVALID_SOCKET;
- OP_ASSERT(*_pnext==_conn);
- *_pnext=_conn->next;
- _conn->next=_stream->free_head;
- _stream->free_head=_conn;
- }
- static void op_http_stream_clear(OpusHTTPStream *_stream){
- while(_stream->lru_head!=NULL){
- op_http_conn_close(_stream,_stream->lru_head,&_stream->lru_head,0);
- }
- if(_stream->ssl_session!=NULL)SSL_SESSION_free(_stream->ssl_session);
- if(_stream->ssl_ctx!=NULL)SSL_CTX_free(_stream->ssl_ctx);
- op_sb_clear(&_stream->response);
- op_sb_clear(&_stream->proxy_connect);
- op_sb_clear(&_stream->request);
- if(_stream->connect_host!=_stream->url.host)_ogg_free(_stream->connect_host);
- op_parsed_url_clear(&_stream->url);
- }
- static int op_http_conn_write_fully(OpusHTTPConn *_conn,
- const char *_buf,int _buf_size){
- struct pollfd fd;
- SSL *ssl_conn;
- fd.fd=_conn->fd;
- ssl_conn=_conn->ssl_conn;
- while(_buf_size>0){
- int err;
- if(ssl_conn!=NULL){
- int ret;
- ret=SSL_write(ssl_conn,_buf,_buf_size);
- if(ret>0){
-
- _buf+=ret;
- _buf_size-=ret;
- continue;
- }
-
- else if(ret==0)return OP_FALSE;
- err=SSL_get_error(ssl_conn,ret);
-
- if(err==SSL_ERROR_WANT_READ)fd.events=POLLIN;
- else if(err==SSL_ERROR_WANT_WRITE)fd.events=POLLOUT;
- else return OP_FALSE;
- }
- else{
- ssize_t ret;
- op_reset_errno();
- ret=send(fd.fd,_buf,_buf_size,0);
- if(ret>0){
- _buf+=ret;
- _buf_size-=ret;
- continue;
- }
- err=op_errno();
- if(err!=EAGAIN&&err!=EWOULDBLOCK)return OP_FALSE;
- fd.events=POLLOUT;
- }
- if(poll(&fd,1,OP_POLL_TIMEOUT_MS)<=0)return OP_FALSE;
- }
- return 0;
- }
- static int op_http_conn_estimate_available(OpusHTTPConn *_conn){
- int available;
- int ret;
- ret=ioctl(_conn->fd,FIONREAD,&available);
- if(ret<0)available=0;
-
- if(_conn->ssl_conn!=NULL)available+=SSL_pending(_conn->ssl_conn);
- return available;
- }
- static opus_int32 op_time_diff_ms(const struct timeb *_end,
- const struct timeb *_start){
- opus_int64 dtime;
- dtime=_end->time-(opus_int64)_start->time;
- OP_ASSERT(_end->millitm<1000);
- OP_ASSERT(_start->millitm<1000);
- if(OP_UNLIKELY(dtime>(OP_INT32_MAX-1000)/1000))return OP_INT32_MAX;
- if(OP_UNLIKELY(dtime<(OP_INT32_MIN+1000)/1000))return OP_INT32_MIN;
- return (opus_int32)dtime*1000+_end->millitm-_start->millitm;
- }
- static void op_http_conn_read_rate_update(OpusHTTPConn *_conn){
- struct timeb read_time;
- opus_int32 read_delta_ms;
- opus_int64 read_delta_bytes;
- opus_int64 read_rate;
- read_delta_bytes=_conn->read_bytes;
- if(read_delta_bytes<=0)return;
- ftime(&read_time);
- read_delta_ms=op_time_diff_ms(&read_time,&_conn->read_time);
- read_rate=_conn->read_rate;
- read_delta_ms=OP_MAX(read_delta_ms,1);
- read_rate+=read_delta_bytes*1000/read_delta_ms-read_rate+4>>3;
- *&_conn->read_time=*&read_time;
- _conn->read_bytes=0;
- _conn->read_rate=read_rate;
- }
- static int op_http_conn_read(OpusHTTPConn *_conn,
- char *_buf,int _buf_size,int _blocking){
- struct pollfd fd;
- SSL *ssl_conn;
- int nread;
- int nread_unblocked;
- fd.fd=_conn->fd;
- ssl_conn=_conn->ssl_conn;
- nread=nread_unblocked=0;
-
- do{
- int err;
- if(ssl_conn!=NULL){
- int ret;
- ret=SSL_read(ssl_conn,_buf+nread,_buf_size-nread);
- OP_ASSERT(ret<=_buf_size-nread);
- if(ret>0){
-
- nread+=ret;
- nread_unblocked+=ret;
- continue;
- }
-
- if(nread>0)break;
- err=SSL_get_error(ssl_conn,ret);
- if(ret==0){
-
- return err==SSL_ERROR_ZERO_RETURN?0:OP_EREAD;
- }
- if(err==SSL_ERROR_WANT_READ)fd.events=POLLIN;
-
- else if(err==SSL_ERROR_WANT_WRITE)fd.events=POLLOUT;
-
- else return OP_EREAD;
- }
- else{
- ssize_t ret;
- op_reset_errno();
- ret=recv(fd.fd,_buf+nread,_buf_size-nread,0);
- OP_ASSERT(ret<=_buf_size-nread);
- if(ret>0){
-
- nread+=ret;
- nread_unblocked+=ret;
- continue;
- }
-
- if(ret==0||nread>0)break;
- err=op_errno();
- if(err!=EAGAIN&&err!=EWOULDBLOCK)return OP_EREAD;
- fd.events=POLLIN;
- }
- _conn->read_bytes+=nread_unblocked;
- op_http_conn_read_rate_update(_conn);
- nread_unblocked=0;
- if(!_blocking)break;
-
- if(poll(&fd,1,OP_POLL_TIMEOUT_MS)<=0)return OP_EREAD;
- }
- while(nread<_buf_size);
- _conn->read_bytes+=nread_unblocked;
- return nread;
- }
- static int op_http_conn_peek(OpusHTTPConn *_conn,char *_buf,int _buf_size){
- struct pollfd fd;
- SSL *ssl_conn;
- int ret;
- fd.fd=_conn->fd;
- ssl_conn=_conn->ssl_conn;
- for(;;){
- int err;
- if(ssl_conn!=NULL){
- ret=SSL_peek(ssl_conn,_buf,_buf_size);
-
- if(ret>=0)return ret;
- err=SSL_get_error(ssl_conn,ret);
- if(err==SSL_ERROR_WANT_READ)fd.events=POLLIN;
-
- else if(err==SSL_ERROR_WANT_WRITE)fd.events=POLLOUT;
- else return 0;
- }
- else{
- op_reset_errno();
- ret=(int)recv(fd.fd,_buf,_buf_size,MSG_PEEK);
-
- if(ret>=0)return ret;
- err=op_errno();
- if(err!=EAGAIN&&err!=EWOULDBLOCK)return 0;
- fd.events=POLLIN;
- }
-
- if(poll(&fd,1,OP_POLL_TIMEOUT_MS)<=0)return 0;
- }
- }
- static int op_http_conn_read_response(OpusHTTPConn *_conn,
- OpusStringBuf *_response){
- int ret;
- _response->nbuf=0;
- ret=op_sb_ensure_capacity(_response,OP_RESPONSE_SIZE_MIN);
- if(OP_UNLIKELY(ret<0))return ret;
- for(;;){
- char *buf;
- int size;
- int capacity;
- int read_limit;
- int terminated;
- size=_response->nbuf;
- capacity=_response->cbuf-1;
- if(OP_UNLIKELY(size>=capacity)){
- ret=op_sb_grow(_response,OP_RESPONSE_SIZE_MAX);
- if(OP_UNLIKELY(ret<0))return ret;
- capacity=_response->cbuf-1;
-
- if(OP_UNLIKELY(size>=capacity))return OP_EIMPL;
- }
- buf=_response->buf;
- ret=op_http_conn_peek(_conn,buf+size,capacity-size);
- if(OP_UNLIKELY(ret<=0))return size<=0?OP_EREAD:OP_FALSE;
-
-
- if(size<4&&op_strncasecmp(buf,"HTTP",OP_MIN(size+ret,4))!=0){
- return OP_FALSE;
- }
-
- buf[size+ret]='\0';
- terminated=0;
- for(read_limit=OP_MAX(size-3,0);read_limit<size+ret;read_limit++){
-
- if(buf[read_limit]=='\n'){
- if(buf[read_limit+1]=='\r'&&OP_LIKELY(buf[read_limit+2]=='\n')){
- terminated=3;
- break;
- }
-
- else if(OP_UNLIKELY(buf[read_limit+1]=='\n')){
- terminated=2;
- break;
- }
- }
- }
- read_limit+=terminated;
- OP_ASSERT(size<=read_limit);
- OP_ASSERT(read_limit<=size+ret);
-
- ret=op_http_conn_read(_conn,buf+size,read_limit-size,1);
- if(OP_UNLIKELY(ret<=0))return OP_FALSE;
- size+=ret;
- buf[size]='\0';
- _response->nbuf=size;
-
- if(terminated&&OP_LIKELY(size>=read_limit))return size;
- }
- return OP_EIMPL;
- }
- # define OP_HTTP_DIGIT "0123456789"
- # define OP_HTTP_CREASON_PHRASE \
- "\001\002\003\004\005\006\007\010\012\013\014\015\016\017\020\021" \
- "\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177"
- # define OP_HTTP_CTLS \
- "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020" \
- "\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177"
- # define OP_HTTP_SEPARATORS " \"(),/:;<=>?@[\\]{}"
- # define OP_HTTP_CTOKEN OP_HTTP_CTLS OP_HTTP_SEPARATORS
- static int op_http_lwsspn(const char *_s){
- int i;
- for(i=0;;){
- if(_s[0]=='\r'&&_s[1]=='\n'&&(_s[2]=='\t'||_s[2]==' '))i+=3;
-
- else if(_s[0]=='\n'&&(_s[1]=='\t'||_s[1]==' '))i+=2;
- else if(_s[i]=='\t'||_s[i]==' ')i++;
- else return i;
- }
- }
- static char *op_http_parse_status_line(int *_v1_1_compat,
- char **_status_code,char *_response){
- char *next;
- char *status_code;
- int v1_1_compat;
- size_t d;
-
-
- OP_ASSERT(op_strncasecmp(_response,"HTTP",4)==0);
- next=_response+4;
- if(OP_UNLIKELY(*next++!='/'))return NULL;
- d=strspn(next,OP_HTTP_DIGIT);
-
- while(*next=='0'){
- next++;
- OP_ASSERT(d>0);
- d--;
- }
-
- if(OP_UNLIKELY(d!=1)||OP_UNLIKELY(*next++!='1'))return NULL;
- if(OP_UNLIKELY(*next++!='.'))return NULL;
- d=strspn(next,OP_HTTP_DIGIT);
- if(OP_UNLIKELY(d<=0))return NULL;
-
- while(*next=='0'){
- next++;
- OP_ASSERT(d>0);
- d--;
- }
-
- v1_1_compat=d>0;
- next+=d;
- if(OP_UNLIKELY(*next++!=' '))return NULL;
- status_code=next;
- d=strspn(next,OP_HTTP_DIGIT);
- if(OP_UNLIKELY(d!=3))return NULL;
- next+=d;
-
- if(OP_UNLIKELY(*next++!=' '))return NULL;
- next+=strcspn(next,OP_HTTP_CREASON_PHRASE);
-
- if(OP_LIKELY(*next=='\r'))next++;
- if(OP_UNLIKELY(*next++!='\n'))return NULL;
- if(_v1_1_compat!=NULL)*_v1_1_compat=v1_1_compat;
- *_status_code=status_code;
- return next;
- }
- static int op_http_get_next_header(char **_header,char **_cdr,char **_s){
- char *header;
- char *header_end;
- char *cdr;
- char *cdr_end;
- char *next;
- size_t d;
- next=*_s;
-
- if(next[0]=='\r'&&next[1]=='\n'||OP_UNLIKELY(next[0]=='\n')){
-
- *_header=NULL;
- *_cdr=NULL;
- *_s=NULL;
- return 0;
- }
- header=next+op_http_lwsspn(next);
- d=strcspn(header,OP_HTTP_CTOKEN);
- if(OP_UNLIKELY(d<=0))return OP_FALSE;
- header_end=header+d;
- next=header_end+op_http_lwsspn(header_end);
- if(OP_UNLIKELY(*next++!=':'))return OP_FALSE;
- next+=op_http_lwsspn(next);
- cdr=next;
- do{
- cdr_end=next+strcspn(next,OP_HTTP_CTLS);
- next=cdr_end+op_http_lwsspn(cdr_end);
- }
- while(next>cdr_end);
-
- if(OP_LIKELY(*next=='\r'))next++;
- if(OP_UNLIKELY(*next++!='\n'))return OP_FALSE;
- *header_end='\0';
- *cdr_end='\0';
-
- op_string_tolower(header);
- *_header=header;
- *_cdr=cdr;
- *_s=next;
- return 0;
- }
- static opus_int64 op_http_parse_nonnegative_int64(const char **_next,
- const char *_cdr){
- const char *next;
- opus_int64 ret;
- int i;
- next=_cdr+strspn(_cdr,OP_HTTP_DIGIT);
- *_next=next;
- if(OP_UNLIKELY(next<=_cdr))return OP_FALSE;
- while(*_cdr=='0')_cdr++;
- if(OP_UNLIKELY(next-_cdr>19))return OP_EIMPL;
- ret=0;
- for(i=0;i<next-_cdr;i++){
- int digit;
- digit=_cdr[i]-'0';
-
- if(OP_UNLIKELY(ret>(OP_INT64_MAX-9)/10+(digit<=7)))return OP_EIMPL;
- ret=ret*10+digit;
- }
- return ret;
- }
- static opus_int64 op_http_parse_content_length(const char *_cdr){
- const char *next;
- opus_int64 content_length;
- content_length=op_http_parse_nonnegative_int64(&next,_cdr);
- if(OP_UNLIKELY(*next!='\0'))return OP_FALSE;
- return content_length;
- }
- static int op_http_parse_content_range(opus_int64 *_first,opus_int64 *_last,
- opus_int64 *_length,const char *_cdr){
- opus_int64 first;
- opus_int64 last;
- opus_int64 length;
- size_t d;
- if(OP_UNLIKELY(op_strncasecmp(_cdr,"bytes",5)!=0))return OP_FALSE;
- _cdr+=5;
- d=op_http_lwsspn(_cdr);
- if(OP_UNLIKELY(d<=0))return OP_FALSE;
- _cdr+=d;
- if(*_cdr!='*'){
- first=op_http_parse_nonnegative_int64(&_cdr,_cdr);
- if(OP_UNLIKELY(first<0))return (int)first;
- _cdr+=op_http_lwsspn(_cdr);
- if(*_cdr++!='-')return OP_FALSE;
- _cdr+=op_http_lwsspn(_cdr);
- last=op_http_parse_nonnegative_int64(&_cdr,_cdr);
- if(OP_UNLIKELY(last<0))return (int)last;
- _cdr+=op_http_lwsspn(_cdr);
- }
- else{
-
- first=last=-1;
- _cdr++;
- }
- if(OP_UNLIKELY(*_cdr++!='/'))return OP_FALSE;
- if(*_cdr!='*'){
- length=op_http_parse_nonnegative_int64(&_cdr,_cdr);
- if(OP_UNLIKELY(length<0))return (int)length;
- }
- else{
-
- _cdr++;
- length=-1;
- }
- if(OP_UNLIKELY(*_cdr!='\0'))return OP_FALSE;
- if(OP_UNLIKELY(last<first))return OP_FALSE;
- if(length>=0&&OP_UNLIKELY(last>=length))return OP_FALSE;
- *_first=first;
- *_last=last;
- *_length=length;
- return 0;
- }
- static int op_http_parse_connection(char *_cdr){
- size_t d;
- int ret;
- ret=0;
- for(;;){
- d=strcspn(_cdr,OP_HTTP_CTOKEN);
- if(OP_UNLIKELY(d<=0))return OP_FALSE;
- if(op_strncasecmp(_cdr,"close",(int)d)==0)ret=1;
-
- _cdr+=d;
- d=op_http_lwsspn(_cdr);
- if(d<=0)break;
- _cdr+=d;
- }
- return OP_UNLIKELY(*_cdr!='\0')?OP_FALSE:ret;
- }
- typedef int (*op_ssl_step_func)(SSL *_ssl_conn);
- static int op_do_ssl_step(SSL *_ssl_conn,op_sock _fd,op_ssl_step_func _step){
- struct pollfd fd;
- fd.fd=_fd;
- for(;;){
- int ret;
- int err;
- ret=(*_step)(_ssl_conn);
- if(ret>=0)return ret;
- err=SSL_get_error(_ssl_conn,ret);
- if(err==SSL_ERROR_WANT_READ)fd.events=POLLIN;
- else if(err==SSL_ERROR_WANT_WRITE)fd.events=POLLOUT;
- else return OP_FALSE;
- if(poll(&fd,1,OP_POLL_TIMEOUT_MS)<=0)return OP_FALSE;
- }
- }
- static int op_bio_retry_write(BIO *_b,const char *_buf,int _num){
- (void)_buf;
- (void)_num;
- BIO_clear_retry_flags(_b);
- BIO_set_retry_write(_b);
- return -1;
- }
- static int op_bio_retry_read(BIO *_b,char *_buf,int _num){
- (void)_buf;
- (void)_num;
- BIO_clear_retry_flags(_b);
- BIO_set_retry_read(_b);
- return -1;
- }
- static int op_bio_retry_puts(BIO *_b,const char *_str){
- return op_bio_retry_write(_b,_str,0);
- }
- static long op_bio_retry_ctrl(BIO *_b,int _cmd,long _num,void *_ptr){
- long ret;
- (void)_b;
- (void)_num;
- (void)_ptr;
- ret=0;
- switch(_cmd){
- case BIO_CTRL_RESET:
- case BIO_C_RESET_READ_REQUEST:{
- BIO_clear_retry_flags(_b);
-
- }
- case BIO_CTRL_EOF:
- case BIO_CTRL_SET:
- case BIO_CTRL_SET_CLOSE:
- case BIO_CTRL_FLUSH:
- case BIO_CTRL_DUP:{
- ret=1;
- }break;
- }
- return ret;
- }
- # if OPENSSL_VERSION_NUMBER<0x10100000L
- # define BIO_set_data(_b,_ptr) ((_b)->ptr=(_ptr))
- # define BIO_set_init(_b,_init) ((_b)->init=(_init))
- # endif
- static int op_bio_retry_new(BIO *_b){
- BIO_set_init(_b,1);
- # if OPENSSL_VERSION_NUMBER<0x10100000L
- _b->num=0;
- # endif
- BIO_set_data(_b,NULL);
- return 1;
- }
- static int op_bio_retry_free(BIO *_b){
- return _b!=NULL;
- }
- # if OPENSSL_VERSION_NUMBER<0x10100000L
- /*This is not const because OpenSSL doesn't allow it, even though it won't
- write to it.*/
- static BIO_METHOD op_bio_retry_method={
- BIO_TYPE_NULL,
- "retry",
- op_bio_retry_write,
- op_bio_retry_read,
- op_bio_retry_puts,
- NULL,
- op_bio_retry_ctrl,
- op_bio_retry_new,
- op_bio_retry_free,
- NULL
- };
- # endif
- /*Establish a CONNECT tunnel and pipeline the start of the TLS handshake for
- proxying https URL requests.*/
- static int op_http_conn_establish_tunnel(OpusHTTPStream *_stream,
- OpusHTTPConn *_conn,op_sock _fd,SSL *_ssl_conn,BIO *_ssl_bio){
- # if OPENSSL_VERSION_NUMBER>=0x10100000L
- BIO_METHOD *bio_retry_method;
- # endif
- BIO *retry_bio;
- char *status_code;
- char *next;
- int ret;
- _conn->ssl_conn=NULL;
- _conn->fd=_fd;
- OP_ASSERT(_stream->proxy_connect.nbuf>0);
- ret=op_http_conn_write_fully(_conn,
- _stream->proxy_connect.buf,_stream->proxy_connect.nbuf);
- if(OP_UNLIKELY(ret<0))return ret;
- # if OPENSSL_VERSION_NUMBER>=0x10100000L
- bio_retry_method=BIO_meth_new(BIO_TYPE_NULL,"retry");
- if(bio_retry_method==NULL)return OP_EFAULT;
- BIO_meth_set_write(bio_retry_method,op_bio_retry_write);
- BIO_meth_set_read(bio_retry_method,op_bio_retry_read);
- BIO_meth_set_puts(bio_retry_method,op_bio_retry_puts);
- BIO_meth_set_ctrl(bio_retry_method,op_bio_retry_ctrl);
- BIO_meth_set_create(bio_retry_method,op_bio_retry_new);
- BIO_meth_set_destroy(bio_retry_method,op_bio_retry_free);
- retry_bio=BIO_new(bio_retry_method);
- if(OP_UNLIKELY(retry_bio==NULL)){
- BIO_meth_free(bio_retry_method);
- return OP_EFAULT;
- }
- # else
- retry_bio=BIO_new(&op_bio_retry_method);
- if(OP_UNLIKELY(retry_bio==NULL))return OP_EFAULT;
- # endif
- SSL_set_bio(_ssl_conn,retry_bio,_ssl_bio);
- SSL_set_connect_state(_ssl_conn);
-
- OP_ALWAYS_TRUE(SSL_connect(_ssl_conn)<0);
- SSL_set_bio(_ssl_conn,_ssl_bio,_ssl_bio);
- # if OPENSSL_VERSION_NUMBER>=0x10100000L
- BIO_meth_free(bio_retry_method);
- # endif
-
- op_sock_set_tcp_nodelay(_fd,1);
- ret=op_http_conn_read_response(_conn,&_stream->response);
- if(OP_UNLIKELY(ret<0))return ret;
- next=op_http_parse_status_line(NULL,&status_code,_stream->response.buf);
-
- if(OP_UNLIKELY(next==NULL)||OP_UNLIKELY(status_code[0]!='2'))return OP_FALSE;
- return 0;
- }
- static int op_http_hostname_match(const char *_host,size_t _host_len,
- ASN1_STRING *_pattern){
- const char *pattern;
- size_t host_label_len;
- size_t host_suffix_len;
- size_t pattern_len;
- size_t pattern_label_len;
- size_t pattern_prefix_len;
- size_t pattern_suffix_len;
- pattern=(const char *)ASN1_STRING_data(_pattern);
- pattern_len=strlen(pattern);
-
- if(OP_UNLIKELY(pattern_len!=(size_t)ASN1_STRING_length(_pattern)))return 0;
- pattern_label_len=strcspn(pattern,".");
- OP_ASSERT(pattern_label_len<=pattern_len);
- pattern_prefix_len=strcspn(pattern,"*");
- if(pattern_prefix_len>=pattern_label_len){
-
- if(pattern_prefix_len<pattern_len)return 0;
-
- return _host_len==pattern_len&&op_strncasecmp(_host,pattern,_host_len)==0;
- }
-
- if(op_strncasecmp(pattern,"xn--",4)==0)return 0;
- host_label_len=strcspn(_host,".");
-
- if(OP_UNLIKELY(_host[host_label_len]!='.')
- ||strchr(_host+host_label_len+1,'.')==NULL){
- return 0;
- }
- OP_ASSERT(host_label_len<_host_len);
-
- if(host_label_len<pattern_label_len)return 0;
- OP_ASSERT(pattern[pattern_prefix_len]=='*');
-
- pattern_suffix_len=pattern_len-pattern_prefix_len-1;
- host_suffix_len=_host_len-host_label_len
- +pattern_label_len-pattern_prefix_len-1;
- return pattern_suffix_len==host_suffix_len
- &&op_strncasecmp(_host,pattern,pattern_prefix_len)==0
- &&op_strncasecmp(_host+_host_len-host_suffix_len,
- pattern+pattern_prefix_len+1,host_suffix_len)==0;
- }
- static struct addrinfo *op_inet_pton(const char *_host){
- struct addrinfo *addrs;
- struct addrinfo hints;
- memset(&hints,0,sizeof(hints));
- hints.ai_socktype=SOCK_STREAM;
- hints.ai_flags=AI_NUMERICHOST;
- if(!getaddrinfo(_host,NULL,&hints,&addrs))return addrs;
- return NULL;
- }
- static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){
- X509 *peer_cert;
- STACK_OF(GENERAL_NAME) *san_names;
- char *host;
- size_t host_len;
- int ret;
- host=_stream->url.host;
- host_len=strlen(host);
- peer_cert=SSL_get_peer_certificate(_ssl_conn);
-
- if(OP_UNLIKELY(peer_cert==NULL))return 0;
- ret=0;
- OP_ASSERT(host_len<INT_MAX);
-
- san_names=X509_get_ext_d2i(peer_cert,NID_subject_alt_name,NULL,NULL);
- if(san_names!=NULL){
- struct addrinfo *addr;
- unsigned char *ip;
- int ip_len;
- int nsan_names;
- int sni;
-
- addr=op_inet_pton(host);
- ip=NULL;
- ip_len=0;
- if(addr!=NULL){
- switch(addr->ai_family){
- case AF_INET:{
- struct sockaddr_in *s;
- s=(struct sockaddr_in *)addr->ai_addr;
- OP_ASSERT(addr->ai_addrlen>=sizeof(*s));
- ip=(unsigned char *)&s->sin_addr;
- ip_len=sizeof(s->sin_addr);
- }break;
- case AF_INET6:{
- struct sockaddr_in6 *s;
- s=(struct sockaddr_in6 *)addr->ai_addr;
- OP_ASSERT(addr->ai_addrlen>=sizeof(*s));
- ip=(unsigned char *)&s->sin6_addr;
- ip_len=sizeof(s->sin6_addr);
- }break;
- }
- }
-
- if(ip==NULL&&strchr(host,'.')==NULL)nsan_names=0;
-
- else nsan_names=sk_GENERAL_NAME_num(san_names);
- for(sni=0;sni<nsan_names;sni++){
- const GENERAL_NAME *name;
- name=sk_GENERAL_NAME_value(san_names,sni);
- if(ip==NULL){
- if(name->type==GEN_DNS
- &&op_http_hostname_match(host,host_len,name->d.dNSName)){
- ret=1;
- break;
- }
- }
- else if(name->type==GEN_IPADD){
- unsigned char *cert_ip;
-
- cert_ip=ASN1_STRING_data(name->d.iPAddress);
- if(ip_len==ASN1_STRING_length(name->d.iPAddress)
- &&memcmp(ip,cert_ip,ip_len)==0){
- ret=1;
- break;
- }
- }
- }
- sk_GENERAL_NAME_pop_free(san_names,GENERAL_NAME_free);
- if(addr!=NULL)freeaddrinfo(addr);
- }
-
- else if(strchr(host,'.')!=NULL){
- int last_cn_loc;
- int cn_loc;
-
- cn_loc=-1;
- do{
- last_cn_loc=cn_loc;
- cn_loc=X509_NAME_get_index_by_NID(X509_get_subject_name(peer_cert),
- NID_commonName,last_cn_loc);
- }
- while(cn_loc>=0);
- ret=last_cn_loc>=0
- &&op_http_hostname_match(host,host_len,
- X509_NAME_ENTRY_get_data(
- X509_NAME_get_entry(X509_get_subject_name(peer_cert),last_cn_loc)));
- }
- X509_free(peer_cert);
- return ret;
- }
- static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
- op_sock _fd,SSL *_ssl_conn){
- SSL_SESSION *ssl_session;
- BIO *ssl_bio;
- int skip_certificate_check;
- int ret;
- ssl_bio=BIO_new_socket(_fd,BIO_NOCLOSE);
- if(OP_LIKELY(ssl_bio==NULL))return OP_FALSE;
- # if !defined(OPENSSL_NO_TLSEXT)
-
- SSL_set_tlsext_host_name(_ssl_conn,_stream->url.host);
- # endif
-
- if(_stream->ssl_session!=NULL){
- SSL_set_session(_ssl_conn,_stream->ssl_session);
- }
-
- if(_stream->proxy_connect.nbuf>0){
- ret=op_http_conn_establish_tunnel(_stream,_conn,
- _fd,_ssl_conn,ssl_bio);
- if(OP_UNLIKELY(ret<0))return ret;
- }
- else{
-
- op_sock_set_tcp_nodelay(_fd,1);
- SSL_set_bio(_ssl_conn,ssl_bio,ssl_bio);
- SSL_set_connect_state(_ssl_conn);
- }
- ret=op_do_ssl_step(_ssl_conn,_fd,SSL_connect);
- if(OP_UNLIKELY(ret<=0))return OP_FALSE;
- ssl_session=_stream->ssl_session;
- skip_certificate_check=_stream->skip_certificate_check;
- if(ssl_session==NULL||!skip_certificate_check){
- ret=op_do_ssl_step(_ssl_conn,_fd,SSL_do_handshake);
- if(OP_UNLIKELY(ret<=0))return OP_FALSE;
-
- if(!skip_certificate_check&&!op_http_verify_hostname(_stream,_ssl_conn)){
- return OP_FALSE;
- }
- if(ssl_session==NULL){
-
- _stream->ssl_session=SSL_get1_session(_ssl_conn);
- }
- }
- _conn->ssl_conn=_ssl_conn;
- _conn->fd=_fd;
- _conn->nrequests_left=OP_PIPELINE_MAX_REQUESTS;
- return 0;
- }
- static int op_sock_connect_next(op_sock _fd,
- const struct addrinfo **_addr,int _ai_family){
- const struct addrinfo *addr;
- int err;
- addr=*_addr;
- for(;;){
-
- for(;addr!=NULL&&addr->ai_family!=_ai_family;addr=addr->ai_next);
- *_addr=addr;
-
- if(addr==NULL)return OP_FALSE;
- if(connect(_fd,addr->ai_addr,addr->ai_addrlen)>=0)return 1;
- err=op_errno();
-
- if(OP_LIKELY(err==EINPROGRESS||err==EWOULDBLOCK))return 0;
- addr=addr->ai_next;
- }
- }
- # define OP_NPROTOS (2)
- static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
- const struct addrinfo *_addrs,struct timeb *_start_time){
- const struct addrinfo *addr;
- const struct addrinfo *addrs[OP_NPROTOS];
- struct pollfd fds[OP_NPROTOS];
- int ai_family;
- int nprotos;
- int ret;
- int pi;
- int pj;
- for(pi=0;pi<OP_NPROTOS;pi++)addrs[pi]=NULL;
-
- for(addr=_addrs,nprotos=0;addr!=NULL&&nprotos<OP_NPROTOS;addr=addr->ai_next){
- if(addr->ai_family==AF_INET6||addr->ai_family==AF_INET){
- OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in6));
- OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in));
-
- for(pi=0;pi<nprotos;pi++)if(addrs[pi]->ai_family==addr->ai_family)break;
- if(pi<nprotos)continue;
- addrs[nprotos++]=addr;
- }
- }
-
- OP_ASSERT(_stream->free_head==_conn);
- _stream->free_head=_conn->next;
- _conn->next=_stream->lru_head;
- _stream->lru_head=_conn;
- ftime(_start_time);
- *&_conn->read_time=*_start_time;
- _conn->read_bytes=0;
- _conn->read_rate=0;
-
- for(pi=0;pi<nprotos;pi++){
- ai_family=addrs[pi]->ai_family;
- fds[pi].fd=socket(ai_family,SOCK_STREAM,addrs[pi]->ai_protocol);
- fds[pi].events=POLLOUT;
- if(OP_LIKELY(fds[pi].fd!=OP_INVALID_SOCKET)){
- if(OP_LIKELY(op_sock_set_nonblocking(fds[pi].fd,1)>=0)){
- ret=op_sock_connect_next(fds[pi].fd,addrs+pi,ai_family);
- if(OP_UNLIKELY(ret>0)){
-
- nprotos=pi+1;
- break;
- }
-
- else if(ret==0)continue;
-
- }
-
- close(fds[pi].fd);
- }
-
- memmove(addrs+pi,addrs+pi+1,sizeof(*addrs)*(nprotos-pi-1));
- nprotos--;
- pi--;
- }
-
- while(pi>=nprotos&&nprotos>0&&poll(fds,nprotos,OP_POLL_TIMEOUT_MS)>0){
- for(pi=0;pi<nprotos;pi++){
- socklen_t errlen;
- int err;
-
- if(!fds[pi].revents)continue;
- errlen=sizeof(err);
-
- ret=getsockopt(fds[pi].fd,SOL_SOCKET,SO_ERROR,&err,&errlen);
- if(ret<0)err=op_errno();
-
- if(err==0||err==EISCONN)break;
-
- ai_family=addrs[pi]->ai_family;
- addrs[pi]=addrs[pi]->ai_next;
- ret=op_sock_connect_next(fds[pi].fd,addrs+pi,ai_family);
-
- if(ret>0)break;
-
- else if(ret==0)continue;
-
- close(fds[pi].fd);
- memmove(fds+pi,fds+pi+1,sizeof(*fds)*(nprotos-pi-1));
- memmove(addrs+pi,addrs+pi+1,sizeof(*addrs)*(nprotos-pi-1));
- nprotos--;
- pi--;
- }
- }
-
- for(pj=0;pj<nprotos;pj++)if(pi!=pj)close(fds[pj].fd);
-
- if(pi>=nprotos)return OP_FALSE;
-
- if(addrs[pi]!=&_stream->addr_info){
- memcpy(&_stream->addr_info,addrs[pi],sizeof(_stream->addr_info));
- _stream->addr_info.ai_addr=&_stream->addr.s;
- _stream->addr_info.ai_next=NULL;
- memcpy(&_stream->addr,addrs[pi]->ai_addr,addrs[pi]->ai_addrlen);
- }
- if(OP_URL_IS_SSL(&_stream->url)){
- SSL *ssl_conn;
-
- OP_ASSERT(_stream->ssl_ctx!=NULL);
- ssl_conn=SSL_new(_stream->ssl_ctx);
- if(OP_LIKELY(ssl_conn!=NULL)){
- ret=op_http_conn_start_tls(_stream,_conn,fds[pi].fd,ssl_conn);
- if(OP_LIKELY(ret>=0))return ret;
- SSL_free(ssl_conn);
- }
- close(fds[pi].fd);
- _conn->fd=OP_INVALID_SOCKET;
- return OP_FALSE;
- }
-
- _conn->ssl_conn=NULL;
- _conn->fd=fds[pi].fd;
- _conn->nrequests_left=OP_PIPELINE_MAX_REQUESTS;
-
- op_sock_set_tcp_nodelay(fds[pi].fd,1);
- return 0;
- }
- static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
- const struct addrinfo *_addrs,struct timeb *_start_time){
- struct timeb resolve_time;
- struct addrinfo *new_addrs;
- int ret;
-
- new_addrs=NULL;
- ftime(&resolve_time);
- if(_addrs!=&_stream->addr_info||op_time_diff_ms(&resolve_time,
- &_stream->resolve_time)>=OP_RESOLVE_CACHE_TIMEOUT_MS){
- new_addrs=op_resolve(_stream->connect_host,_stream->connect_port);
- if(OP_LIKELY(new_addrs!=NULL)){
- _addrs=new_addrs;
- *&_stream->resolve_time=*&resolve_time;
- }
- else if(OP_LIKELY(_addrs==NULL))return OP_FALSE;
- }
- ret=op_http_connect_impl(_stream,_conn,_addrs,_start_time);
- if(new_addrs!=NULL)freeaddrinfo(new_addrs);
- return ret;
- }
- # define OP_BASE64_LENGTH(_len) (((_len)+2)/3*4)
- static const char BASE64_TABLE[64]={
- 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
- 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
- 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
- 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
- };
- static char *op_base64_encode(char *_dst,const char *_src,int _len){
- unsigned s0;
- unsigned s1;
- unsigned s2;
- int ngroups;
- int i;
- ngroups=_len/3;
- for(i=0;i<ngroups;i++){
- s0=_src[3*i+0];
- s1=_src[3*i+1];
- s2=_src[3*i+2];
- _dst[4*i+0]=BASE64_TABLE[s0>>2];
- _dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4];
- _dst[4*i+2]=BASE64_TABLE[(s1&15)<<2|s2>>6];
- _dst[4*i+3]=BASE64_TABLE[s2&63];
- }
- _len-=3*i;
- if(_len==1){
- s0=_src[3*i+0];
- _dst[4*i+0]=BASE64_TABLE[s0>>2];
- _dst[4*i+1]=BASE64_TABLE[(s0&3)<<4];
- _dst[4*i+2]='=';
- _dst[4*i+3]='=';
- i++;
- }
- else if(_len==2){
- s0=_src[3*i+0];
- s1=_src[3*i+1];
- _dst[4*i+0]=BASE64_TABLE[s0>>2];
- _dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4];
- _dst[4*i+2]=BASE64_TABLE[(s1&15)<<2];
- _dst[4*i+3]='=';
- i++;
- }
- _dst[4*i]='\0';
- return _dst+4*i;
- }
- static int op_sb_append_basic_auth_header(OpusStringBuf *_sb,
- const char *_header,const char *_user,const char *_pass){
- int user_len;
- int pass_len;
- int user_pass_len;
- int base64_len;
- int nbuf_total;
- int ret;
- ret=op_sb_append_string(_sb,_header);
- ret|=op_sb_append(_sb,": Basic ",8);
- user_len=strlen(_user);
- pass_len=strlen(_pass);
- if(OP_UNLIKELY(pass_len>INT_MAX-user_len))return OP_EFAULT;
- if(OP_UNLIKELY(user_len+pass_len>(INT_MAX>>2)*3-3))return OP_EFAULT;
- user_pass_len=user_len+1+pass_len;
- base64_len=OP_BASE64_LENGTH(user_pass_len);
-
- nbuf_total=_sb->nbuf;
- if(OP_UNLIKELY(base64_len>INT_MAX-nbuf_total))return OP_EFAULT;
- nbuf_total+=base64_len;
- ret|=op_sb_ensure_capacity(_sb,nbuf_total);
- if(OP_UNLIKELY(ret<0))return ret;
- _sb->nbuf=nbuf_total-user_pass_len;
- OP_ALWAYS_TRUE(!op_sb_append(_sb,_user,user_len));
- OP_ALWAYS_TRUE(!op_sb_append(_sb,":",1));
- OP_ALWAYS_TRUE(!op_sb_append(_sb,_pass,pass_len));
- op_base64_encode(_sb->buf+nbuf_total-base64_len,
- _sb->buf+nbuf_total-user_pass_len,user_pass_len);
- return op_sb_append(_sb,"\r\n",2);
- }
- static int op_http_allow_pipelining(const char *_server){
-
- static const char *BAD_SERVERS[]={
- "EFAServer/",
- "Microsoft-IIS/4.",
- "Microsoft-IIS/5.",
- "Netscape-Enterprise/3.",
- "Netscape-Enterprise/4.",
- "Netscape-Enterprise/5.",
- "Netscape-Enterprise/6.",
- "WebLogic 3.",
- "WebLogic 4.",
- "WebLogic 5.",
- "WebLogic 6.",
- "Winstone Servlet Engine v0."
- };
- # define NBAD_SERVERS ((int)(sizeof(BAD_SERVERS)/sizeof(*BAD_SERVERS)))
- if(*_server>='E'&&*_server<='W'){
- int si;
- for(si=0;si<NBAD_SERVERS;si++){
- if(strncmp(_server,BAD_SERVERS[si],strlen(BAD_SERVERS[si]))==0){
- return 0;
- }
- }
- }
- return 1;
- # undef NBAD_SERVERS
- }
- static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
- int _skip_certificate_check,const char *_proxy_host,unsigned _proxy_port,
- const char *_proxy_user,const char *_proxy_pass,OpusServerInfo *_info){
- struct addrinfo *addrs;
- int nredirs;
- int ret;
- #if defined(_WIN32)
- op_init_winsock();
- #endif
- ret=op_parse_url(&_stream->url,_url);
- if(OP_UNLIKELY(ret<0))return ret;
- if(_proxy_host!=NULL){
- if(OP_UNLIKELY(_proxy_port>65535U))return OP_EINVAL;
- _stream->connect_host=op_string_dup(_proxy_host);
- _stream->connect_port=_proxy_port;
- }
- else{
- _stream->connect_host=_stream->url.host;
- _stream->connect_port=_stream->url.port;
- }
- addrs=NULL;
- for(nredirs=0;nredirs<OP_REDIRECT_LIMIT;nredirs++){
- OpusParsedURL next_url;
- struct timeb start_time;
- struct timeb end_time;
- char *next;
- char *status_code;
- int minor_version_pos;
- int v1_1_compat;
-
- if(OP_URL_IS_SSL(&_stream->url)&&_stream->ssl_ctx==NULL){
- SSL_CTX *ssl_ctx;
- # if OPENSSL_VERSION_NUMBER<0x10100000L
- # if !defined(OPENSSL_NO_LOCKING)
- /*The documentation says SSL_library_init() is not reentrant.
- We don't want to add our own depenencies on a threading library, and it
- appears that it's safe to call OpenSSL's locking functions before the
- library is initialized, so that's what we'll do (really OpenSSL should
- do this for us).
- This doesn't guarantee that _other_ threads in the application aren't
- calling SSL_library_init() at the same time, but there's not much we
- can do about that.*/
- CRYPTO_w_lock(CRYPTO_LOCK_SSL);
- # endif
- SSL_library_init();
- /*Needed to get SHA2 algorithms with old OpenSSL versions.*/
- OpenSSL_add_ssl_algorithms();
- # if !defined(OPENSSL_NO_LOCKING)
- CRYPTO_w_unlock(CRYPTO_LOCK_SSL);
- # endif
- # else
- /*Finally, OpenSSL does this for us, but as penance, it can now fail.*/
- if(!OPENSSL_init_ssl(0,NULL))return OP_EFAULT;
- # endif
- ssl_ctx=SSL_CTX_new(SSLv23_client_method());
- if(ssl_ctx==NULL)return OP_EFAULT;
- if(!_skip_certificate_check){
- /*We don't do anything if this fails, since it just means we won't load
- any certificates (and thus all checks will fail).
- However, as that is probably the result of a system
- mis-configuration, assert here to make it easier to identify.*/
- OP_ALWAYS_TRUE(SSL_CTX_set_default_verify_paths(ssl_ctx));
- SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,NULL);
- }
- _stream->ssl_ctx=ssl_ctx;
- _stream->skip_certificate_check=_skip_certificate_check;
- if(_proxy_host!=NULL){
-
- _stream->proxy_connect.nbuf=0;
- ret=op_sb_append(&_stream->proxy_connect,"CONNECT ",8);
- ret|=op_sb_append_string(&_stream->proxy_connect,_stream->url.host);
- ret|=op_sb_append_port(&_stream->proxy_connect,_stream->url.port);
-
- ret|=op_sb_append(&_stream->proxy_connect," HTTP/1.1\r\n",11);
- ret|=op_sb_append(&_stream->proxy_connect,"Host: ",6);
- ret|=op_sb_append_string(&_stream->proxy_connect,_stream->url.host);
-
- ret|=op_sb_append_port(&_stream->proxy_connect,_stream->url.port);
- ret|=op_sb_append(&_stream->proxy_connect,"\r\n",2);
- ret|=op_sb_append(&_stream->proxy_connect,"User-Agent: .\r\n",15);
- if(_proxy_user!=NULL&&_proxy_pass!=NULL){
- ret|=op_sb_append_basic_auth_header(&_stream->proxy_connect,
- "Proxy-Authorization",_proxy_user,_proxy_pass);
- }
-
- ret|=op_sb_append(&_stream->proxy_connect,
- "Proxy-Connection: keep-alive\r\n",30);
- ret|=op_sb_append(&_stream->proxy_connect,"\r\n",2);
- if(OP_UNLIKELY(ret<0))return ret;
- }
- }
-
- ret=op_http_connect(_stream,_stream->conns+0,addrs,&start_time);
- if(OP_UNLIKELY(ret<0))return ret;
-
- _stream->request.nbuf=0;
- ret=op_sb_append(&_stream->request,"GET ",4);
- ret|=op_sb_append_string(&_stream->request,
- _proxy_host!=NULL?_url:_stream->url.path);
-
- ret|=op_sb_append(&_stream->request," HTTP/1.0\r\n",11);
-
- minor_version_pos=_stream->request.nbuf-3;
- ret|=op_sb_append(&_stream->request,"Host: ",6);
- ret|=op_sb_append_string(&_stream->request,_stream->url.host);
- if(!OP_URL_IS_DEFAULT_PORT(&_stream->url)){
- ret|=op_sb_append_port(&_stream->request,_stream->url.port);
- }
- ret|=op_sb_append(&_stream->request,"\r\n",2);
-
- ret|=op_sb_append(&_stream->request,"User-Agent: .\r\n",15);
- if(_proxy_host!=NULL&&!OP_URL_IS_SSL(&_stream->url)
- &&_proxy_user!=NULL&&_proxy_pass!=NULL){
- ret|=op_sb_append_basic_auth_header(&_stream->request,
- "Proxy-Authorization",_proxy_user,_proxy_pass);
- }
- if(_stream->url.user!=NULL&&_stream->url.pass!=NULL){
- ret|=op_sb_append_basic_auth_header(&_stream->request,
- "Authorization",_stream->url.user,_stream->url.pass);
- }
-
-
- ret|=op_sb_append(&_stream->request,"Referer: /\r\n",12);
-
- ret|=op_sb_append(&_stream->request,"Range: bytes=0-\r\n",17);
-
- _stream->request_tail=_stream->request.nbuf-4;
- ret|=op_sb_append(&_stream->request,"\r\n",2);
- if(OP_UNLIKELY(ret<0))return ret;
- ret=op_http_conn_write_fully(_stream->conns+0,
- _stream->request.buf,_stream->request.nbuf);
- if(OP_UNLIKELY(ret<0))return ret;
- ret=op_http_conn_read_response(_stream->conns+0,&_stream->response);
- if(OP_UNLIKELY(ret<0))return ret;
- ftime(&end_time);
- next=op_http_parse_status_line(&v1_1_compat,&status_code,
- _stream->response.buf);
- if(OP_UNLIKELY(next==NULL))return OP_FALSE;
- if(status_code[0]=='2'){
- opus_int64 content_length;
- opus_int64 range_length;
- int pipeline_supported;
- int pipeline_disabled;
-
- if(status_code[1]!='0')return OP_FALSE;
- content_length=-1;
- range_length=-1;
-
- pipeline_supported=0;
- pipeline_disabled=0;
- for(;;){
- char *header;
- char *cdr;
- ret=op_http_get_next_header(&header,&cdr,&next);
- if(OP_UNLIKELY(ret<0))return ret;
- if(header==NULL)break;
- if(strcmp(header,"content-length")==0){
-
- if(OP_UNLIKELY(content_length>=0))return OP_FALSE;
- content_length=op_http_parse_content_length(cdr);
- if(OP_UNLIKELY(content_length<0))return (int)content_length;
-
- if(range_length>=0&&OP_UNLIKELY(content_length!=range_length)){
- return OP_FALSE;
- }
- }
- else if(strcmp(header,"content-range")==0){
- opus_int64 range_first;
- opus_int64 range_last;
-
- if(OP_UNLIKELY(range_length>=0))return OP_FALSE;
- ret=op_http_parse_content_range(&range_first,&range_last,
- &range_length,cdr);
- if(OP_UNLIKELY(ret<0))return ret;
-
- if(status_code[2]=='6'
- &&(OP_UNLIKELY(range_first<0)||OP_UNLIKELY(range_last<0))){
- return OP_FALSE;
- }
-
- if(range_length>=0){
-
- if(range_last>=0&&OP_UNLIKELY(range_last!=range_length-1)){
- return OP_FALSE;
- }
- }
-
- else if(range_last>=0)range_length=range_last+1;
-
- if(content_length>=0&&OP_UNLIKELY(content_length!=range_length)){
- return OP_FALSE;
- }
- }
- else if(strcmp(header,"connection")==0){
-
- ret=op_http_parse_connection(cdr);
- if(OP_UNLIKELY(ret<0))return ret;
- pipeline_disabled|=ret;
- }
- else if(strcmp(header,"server")==0){
-
- pipeline_supported=v1_1_compat;
- if(v1_1_compat)pipeline_disabled|=!op_http_allow_pipelining(cdr);
- if(_info!=NULL&&_info->server==NULL)_info->server=op_string_dup(cdr);
- }
-
- else if(_info!=NULL){
- if(strcmp(header,"content-type")==0){
- if(_info->content_type==NULL){
- _info->content_type=op_string_dup(cdr);
- }
- }
- else if(header[0]=='i'&&header[1]=='c'
- &&(header[2]=='e'||header[2]=='y')&&header[3]=='-'){
- if(strcmp(header+4,"name")==0){
- if(_info->name==NULL)_info->name=op_string_dup(cdr);
- }
- else if(strcmp(header+4,"description")==0){
- if(_info->description==NULL)_info->description=op_string_dup(cdr);
- }
- else if(strcmp(header+4,"genre")==0){
- if(_info->genre==NULL)_info->genre=op_string_dup(cdr);
- }
- else if(strcmp(header+4,"url")==0){
- if(_info->url==NULL)_info->url=op_string_dup(cdr);
- }
- else if(strcmp(header,"icy-br")==0
- ||strcmp(header,"ice-bitrate")==0){
- if(_info->bitrate_kbps<0){
- opus_int64 bitrate_kbps;
-
- bitrate_kbps=op_http_parse_content_length(cdr);
- if(bitrate_kbps>=0&&bitrate_kbps<=OP_INT32_MAX){
- _info->bitrate_kbps=(opus_int32)bitrate_kbps;
- }
- }
- }
- else if(strcmp(header,"icy-pub")==0
- ||strcmp(header,"ice-public")==0){
- if(_info->is_public<0&&(cdr[0]=='0'||cdr[0]=='1')&&cdr[1]=='\0'){
- _info->is_public=cdr[0]-'0';
- }
- }
- }
- }
- }
- switch(status_code[2]){
-
- case '0':break;
-
- case '3':break;
-
- case '4':{
- if(content_length>=0&&OP_UNLIKELY(content_length!=0)){
- return OP_FALSE;
- }
- }break;
-
- case '6':{
-
- if(OP_UNLIKELY(range_length<0))return OP_FALSE;
- content_length=range_length;
-
- _stream->seekable=1;
- }break;
-
- default:return OP_FALSE;
- }
- _stream->content_length=content_length;
- _stream->pipeline=pipeline_supported&&!pipeline_disabled;
-
- if(_stream->pipeline)_stream->request.buf[minor_version_pos]='1';
- _stream->conns[0].pos=0;
- _stream->conns[0].end_pos=_stream->seekable?content_length:-1;
- _stream->conns[0].chunk_size=-1;
- _stream->cur_conni=0;
- _stream->connect_rate=op_time_diff_ms(&end_time,&start_time);
- _stream->connect_rate=OP_MAX(_stream->connect_rate,1);
- if(_info!=NULL)_info->is_ssl=OP_URL_IS_SSL(&_stream->url);
-
- return 0;
- }
-
- else if(status_code[0]!='3')return OP_FALSE;
-
-
- if(status_code[1]!='0')return OP_FALSE;
- switch(status_code[2]){
-
- case '0':
-
- case '1':
-
- case '2':
-
- case '7':
-
- case '8':break;
-
- case '5':return OP_EIMPL;
-
- default:return OP_FALSE;
- }
- _url=NULL;
- for(;;){
- char *header;
- char *cdr;
- ret=op_http_get_next_header(&header,&cdr,&next);
- if(OP_UNLIKELY(ret<0))return ret;
- if(header==NULL)break;
- if(strcmp(header,"location")==0&&OP_LIKELY(_url==NULL))_url=cdr;
- }
- if(OP_UNLIKELY(_url==NULL))return OP_FALSE;
- ret=op_parse_url(&next_url,_url);
- if(OP_UNLIKELY(ret<0))return ret;
- if(_proxy_host==NULL||_stream->ssl_session!=NULL){
- if(strcmp(_stream->url.host,next_url.host)==0
- &&_stream->url.port==next_url.port){
-
- addrs=&_stream->addr_info;
- }
- else{
- if(_stream->ssl_session!=NULL){
-
- SSL_SESSION_free(_stream->ssl_session);
- _stream->ssl_session=NULL;
- }
- }
- }
- if(_proxy_host==NULL){
- OP_ASSERT(_stream->connect_host==_stream->url.host);
- _stream->connect_host=next_url.host;
- _stream->connect_port=next_url.port;
- }
-
- else addrs=&_stream->addr_info;
- op_parsed_url_clear(&_stream->url);
- *&_stream->url=*&next_url;
-
- op_http_conn_close(_stream,_stream->conns+0,&_stream->lru_head,1);
- }
-
- return OP_FALSE;
- }
- static int op_http_conn_send_request(OpusHTTPStream *_stream,
- OpusHTTPConn *_conn,opus_int64 _pos,opus_int32 _chunk_size,
- int _try_not_to_block){
- opus_int64 next_end;
- int ret;
-
- OP_ASSERT(_conn->next_pos<0);
-
- OP_ASSERT(_stream->request.nbuf>=_stream->request_tail);
- _stream->request.nbuf=_stream->request_tail;
- ret=op_sb_append_nonnegative_int64(&_stream->request,_pos);
- ret|=op_sb_append(&_stream->request,"-",1);
- if(_chunk_size>0&&OP_ADV_OFFSET(_pos,2*_chunk_size)<_stream->content_length){
-
- OP_ASSERT(_stream->pipeline);
- next_end=_pos+_chunk_size;
- ret|=op_sb_append_nonnegative_int64(&_stream->request,next_end-1);
-
- _chunk_size<<=1;
-
- if(_chunk_size>OP_PIPELINE_CHUNK_SIZE_MAX)_chunk_size=-1;
- }
- else{
-
- next_end=-1;
- _chunk_size=-1;
- }
- ret|=op_sb_append(&_stream->request,"\r\n\r\n",4);
- if(OP_UNLIKELY(ret<0))return ret;
-
- if(_try_not_to_block){
- # if defined(FIONSPACE)
- int available;
- ret=ioctl(_conn->fd,FIONSPACE,&available);
- if(ret<0||available<_stream->request.nbuf)return 1;
- # endif
- }
- ret=op_http_conn_write_fully(_conn,
- _stream->request.buf,_stream->request.nbuf);
- if(OP_UNLIKELY(ret<0))return ret;
- _conn->next_pos=_pos;
- _conn->next_end=next_end;
-
- _conn->chunk_size=_chunk_size;
- _conn->nrequests_left--;
- return ret;
- }
- static int op_http_conn_handle_response(OpusHTTPStream *_stream,
- OpusHTTPConn *_conn){
- char *next;
- char *status_code;
- opus_int64 range_length;
- opus_int64 next_pos;
- opus_int64 next_end;
- int ret;
- ret=op_http_conn_read_response(_conn,&_stream->response);
-
- if(OP_UNLIKELY(ret<0))return ret==OP_EREAD?1:ret;
- next=op_http_parse_status_line(NULL,&status_code,_stream->response.buf);
- if(OP_UNLIKELY(next==NULL))return OP_FALSE;
-
- if(strncmp(status_code,"206",3)!=0){
-
- return strncmp(status_code,"408",3)==0?1:OP_FALSE;
- }
- next_pos=_conn->next_pos;
- next_end=_conn->next_end;
- range_length=-1;
- for(;;){
- char *header;
- char *cdr;
- ret=op_http_get_next_header(&header,&cdr,&next);
- if(OP_UNLIKELY(ret<0))return ret;
- if(header==NULL)break;
- if(strcmp(header,"content-range")==0){
- opus_int64 range_first;
- opus_int64 range_last;
-
- if(OP_UNLIKELY(range_length>=0))return OP_FALSE;
- ret=op_http_parse_content_range(&range_first,&range_last,
- &range_length,cdr);
- if(OP_UNLIKELY(ret<0))return ret;
-
- if(OP_UNLIKELY(range_first<0)||OP_UNLIKELY(range_last<0))return OP_FALSE;
-
- if(OP_UNLIKELY(range_last>=OP_INT64_MAX))return OP_FALSE;
- range_last++;
-
- if(range_first!=next_pos)return OP_FALSE;
- if(next_end<0){
-
- if(range_length>=0){
-
- if(OP_UNLIKELY(range_last!=range_length))return OP_FALSE;
- }
-
- else range_length=range_last;
- next_end=range_last;
- }
- else{
- if(range_last!=next_end)return OP_FALSE;
-
- if(range_length<0){
- range_length=OP_MAX(range_last,_stream->content_length);
- }
- }
- }
- else if(strcmp(header,"content-length")==0){
- opus_int64 content_length;
-
- content_length=op_http_parse_content_length(cdr);
- if(OP_UNLIKELY(content_length<0))return (int)content_length;
- if(next_end<0){
-
- if(OP_UNLIKELY(next_pos>OP_INT64_MAX-content_length))return OP_FALSE;
- next_end=next_pos+content_length;
- }
-
- else if(OP_UNLIKELY(next_end-next_pos!=content_length))return OP_FALSE;
- }
- else if(strcmp(header,"connection")==0){
- ret=op_http_parse_connection(cdr);
- if(OP_UNLIKELY(ret<0))return ret;
-
- if(OP_UNLIKELY(ret>0))_conn->nrequests_left=0;
- }
- }
-
- if(OP_UNLIKELY(range_length<0))return OP_FALSE;
-
- _stream->content_length=range_length;
- _conn->pos=next_pos;
- _conn->end_pos=next_end;
- _conn->next_pos=-1;
- return 0;
- }
- static int op_http_conn_open_pos(OpusHTTPStream *_stream,
- OpusHTTPConn *_conn,opus_int64 _pos,opus_int32 _chunk_size){
- struct timeb start_time;
- struct timeb end_time;
- opus_int32 connect_rate;
- opus_int32 connect_time;
- int ret;
- ret=op_http_connect(_stream,_conn,&_stream->addr_info,&start_time);
- if(OP_UNLIKELY(ret<0))return ret;
- ret=op_http_conn_send_request(_stream,_conn,_pos,_chunk_size,0);
- if(OP_UNLIKELY(ret<0))return ret;
- ret=op_http_conn_handle_response(_stream,_conn);
- if(OP_UNLIKELY(ret!=0))return OP_FALSE;
- ftime(&end_time);
- _stream->cur_conni=_conn-_stream->conns;
- OP_ASSERT(_stream->cur_conni>=0&&_stream->cur_conni<OP_NCONNS_MAX);
-
- connect_time=op_time_diff_ms(&end_time,&start_time);
- connect_rate=_stream->connect_rate;
- connect_rate+=OP_MAX(connect_time,1)-connect_rate+8>>4;
- _stream->connect_rate=connect_rate;
- return 0;
- }
- static int op_http_conn_read_body(OpusHTTPStream *_stream,
- OpusHTTPConn *_conn,unsigned char *_buf,int _buf_size){
- opus_int64 pos;
- opus_int64 end_pos;
- opus_int64 next_pos;
- opus_int64 content_length;
- int nread;
- int pipeline;
- int ret;
-
- OP_ASSERT(_stream->lru_head==_conn);
-
- OP_ASSERT(_buf_size>0);
- pos=_conn->pos;
- end_pos=_conn->end_pos;
- next_pos=_conn->next_pos;
- pipeline=_stream->pipeline;
- content_length=_stream->content_length;
- if(end_pos>=0){
-
- if(pos>=end_pos){
- OP_ASSERT(content_length>=0);
-
- if(content_length<=end_pos)return 0;
-
- if(next_pos<0){
-
- if(!pipeline||_conn->nrequests_left<=0){
-
- OP_ASSERT(_stream->seekable);
-
- op_http_conn_close(_stream,_conn,&_stream->lru_head,1);
-
- OP_ASSERT(pipeline||_conn->chunk_size==-1);
- ret=op_http_conn_open_pos(_stream,_conn,end_pos,_conn->chunk_size);
- if(OP_UNLIKELY(ret<0))return OP_EREAD;
- }
- else{
-
- ret=op_http_conn_send_request(_stream,_conn,pos,_conn->chunk_size,0);
- if(OP_UNLIKELY(ret<0))return OP_EREAD;
- next_pos=_conn->next_pos;
- OP_ASSERT(next_pos>=0);
- }
- }
- if(next_pos>=0){
-
- OP_ASSERT(next_pos==end_pos);
- ret=op_http_conn_handle_response(_stream,_conn);
- if(OP_UNLIKELY(ret<0))return OP_EREAD;
- if(OP_UNLIKELY(ret>0)&&pipeline){
- opus_int64 next_end;
- next_end=_conn->next_end;
-
- op_http_conn_close(_stream,_conn,&_stream->lru_head,1);
-
- OP_ASSERT(next_end<0
- ||next_end-next_pos>=0&&next_end-next_pos<=OP_INT32_MAX);
- ret=op_http_conn_open_pos(_stream,_conn,next_pos,
- next_end<0?-1:(opus_int32)(next_end-next_pos));
- if(OP_UNLIKELY(ret<0))return OP_EREAD;
- }
- else if(OP_UNLIKELY(ret!=0))return OP_EREAD;
- }
- pos=_conn->pos;
- end_pos=_conn->end_pos;
- content_length=_stream->content_length;
- }
- OP_ASSERT(end_pos>pos);
- _buf_size=OP_MIN(_buf_size,end_pos-pos);
- }
- nread=op_http_conn_read(_conn,(char *)_buf,_buf_size,1);
- if(OP_UNLIKELY(nread<0))return nread;
- pos+=nread;
- _conn->pos=pos;
- OP_ASSERT(end_pos<0||content_length>=0);
-
- if(end_pos>=0&&end_pos<content_length&&next_pos<0
- &&pipeline&&OP_LIKELY(_conn->nrequests_left>0)){
- opus_int64 request_thresh;
- opus_int32 chunk_size;
-
- request_thresh=_stream->connect_rate*_conn->read_rate>>12;
-
- chunk_size=_conn->chunk_size;
- if(chunk_size>=0)request_thresh=OP_MIN(chunk_size>>2,request_thresh);
- if(end_pos-pos<request_thresh){
- ret=op_http_conn_send_request(_stream,_conn,end_pos,_conn->chunk_size,1);
- if(OP_UNLIKELY(ret<0))return OP_EREAD;
- }
- }
- return nread;
- }
- static int op_http_stream_read(void *_stream,
- unsigned char *_ptr,int _buf_size){
- OpusHTTPStream *stream;
- ptrdiff_t nread;
- opus_int64 size;
- opus_int64 pos;
- int ci;
- stream=(OpusHTTPStream *)_stream;
-
- if(_buf_size<=0)return 0;
- ci=stream->cur_conni;
-
- if(ci<0)return 0;
- pos=stream->conns[ci].pos;
- size=stream->content_length;
-
- if(size>=0){
- if(pos>=size)return 0;
-
- if(_buf_size>size-pos)_buf_size=(int)(size-pos);
- }
- nread=op_http_conn_read_body(stream,stream->conns+ci,_ptr,_buf_size);
- if(OP_UNLIKELY(nread<=0)){
-
- op_http_conn_close(stream,stream->conns+ci,&stream->lru_head,1);
- stream->cur_conni=-1;
- stream->pos=pos;
- }
- return nread;
- }
- static int op_http_conn_read_ahead(OpusHTTPStream *_stream,
- OpusHTTPConn *_conn,int _just_read_ahead,opus_int64 _target){
- opus_int64 pos;
- opus_int64 end_pos;
- opus_int64 next_pos;
- opus_int64 next_end;
- ptrdiff_t nread;
- int ret;
- pos=_conn->pos;
- end_pos=_conn->end_pos;
- next_pos=_conn->next_pos;
- next_end=_conn->next_end;
- if(!_just_read_ahead){
-
- OP_ASSERT(_stream->pipeline);
- _conn->next_pos=-1;
- ret=op_http_conn_send_request(_stream,_conn,_target,
- OP_PIPELINE_CHUNK_SIZE,0);
- if(OP_UNLIKELY(ret<0))return ret;
- }
-
- if(_just_read_ahead&&(end_pos<0||_target<end_pos))end_pos=_target;
- else if(next_pos>=0){
- opus_int64 next_next_pos;
- opus_int64 next_next_end;
-
- while(pos<end_pos){
- nread=op_http_conn_read(_conn,_stream->response.buf,
- (int)OP_MIN(end_pos-pos,_stream->response.cbuf),1);
-
- if(nread<=0)return OP_FALSE;
- pos+=nread;
- }
- OP_ASSERT(pos==end_pos);
- if(_just_read_ahead){
- next_next_pos=next_next_end=-1;
- end_pos=_target;
- }
- else{
- OP_ASSERT(_conn->next_pos==_target);
- next_next_pos=_target;
- next_next_end=_conn->next_end;
- _conn->next_pos=next_pos;
- _conn->next_end=next_end;
- end_pos=next_end;
- }
- ret=op_http_conn_handle_response(_stream,_conn);
- if(OP_UNLIKELY(ret!=0))return OP_FALSE;
- _conn->next_pos=next_next_pos;
- _conn->next_end=next_next_end;
- }
- while(pos<end_pos){
- nread=op_http_conn_read(_conn,_stream->response.buf,
- (int)OP_MIN(end_pos-pos,_stream->response.cbuf),1);
-
- if(nread<=0)return OP_FALSE;
- pos+=nread;
- }
- OP_ASSERT(pos==end_pos);
- if(!_just_read_ahead){
- ret=op_http_conn_handle_response(_stream,_conn);
- if(OP_UNLIKELY(ret!=0))return OP_FALSE;
- }
- else _conn->pos=end_pos;
- OP_ASSERT(_conn->pos==_target);
- return 0;
- }
- static int op_http_stream_seek(void *_stream,opus_int64 _offset,int _whence){
- struct timeb seek_time;
- OpusHTTPStream *stream;
- OpusHTTPConn *conn;
- OpusHTTPConn **pnext;
- OpusHTTPConn *close_conn;
- OpusHTTPConn **close_pnext;
- opus_int64 content_length;
- opus_int64 pos;
- int pipeline;
- int ci;
- int ret;
- stream=(OpusHTTPStream *)_stream;
- if(!stream->seekable)return -1;
- content_length=stream->content_length;
-
- OP_ASSERT(content_length>=0);
- ci=stream->cur_conni;
- pos=ci<0?content_length:stream->conns[ci].pos;
- switch(_whence){
- case SEEK_SET:{
-
- if(_offset<0)return -1;
- pos=_offset;
- }break;
- case SEEK_CUR:{
-
- if(_offset<-pos||_offset>OP_INT64_MAX-pos)return -1;
- pos+=_offset;
- }break;
- case SEEK_END:{
-
- if(_offset>content_length||_offset<content_length-OP_INT64_MAX)return -1;
- pos=content_length-_offset;
- }break;
- default:return -1;
- }
-
- if(ci>=0){
- op_http_conn_read_rate_update(stream->conns+ci);
- *&seek_time=*&stream->conns[ci].read_time;
- }
- else ftime(&seek_time);
-
- if(pos>=content_length){
- stream->cur_conni=-1;
- stream->pos=pos;
- return 0;
- }
-
- pnext=&stream->lru_head;
- conn=stream->lru_head;
- while(conn!=NULL){
- opus_int64 conn_pos;
- opus_int64 end_pos;
- int available;
-
- if(op_time_diff_ms(&seek_time,&conn->read_time)>
- OP_CONNECTION_IDLE_TIMEOUT_MS
- ||conn->nrequests_left<OP_PIPELINE_MIN_REQUESTS){
- op_http_conn_close(stream,conn,pnext,1);
- conn=*pnext;
- continue;
- }
- available=op_http_conn_estimate_available(conn);
- conn_pos=conn->pos;
- end_pos=conn->end_pos;
- if(conn->next_pos>=0){
- OP_ASSERT(end_pos>=0);
- OP_ASSERT(conn->next_pos==end_pos);
- end_pos=conn->next_end;
- }
- OP_ASSERT(end_pos<0||conn_pos<=end_pos);
-
- if(conn_pos<=pos&&pos-conn_pos<=available&&(end_pos<0||pos<end_pos)){
-
- ret=op_http_conn_read_ahead(stream,conn,1,pos);
- if(OP_UNLIKELY(ret<0)){
-
- op_http_conn_close(stream,conn,pnext,1);
- conn=*pnext;
- continue;
- }
-
- *pnext=conn->next;
- conn->next=stream->lru_head;
- stream->lru_head=conn;
- stream->cur_conni=conn-stream->conns;
- return 0;
- }
- pnext=&conn->next;
- conn=conn->next;
- }
-
- close_pnext=NULL;
- close_conn=NULL;
- pnext=&stream->lru_head;
- conn=stream->lru_head;
- pipeline=stream->pipeline;
- while(conn!=NULL){
- opus_int64 conn_pos;
- opus_int64 end_pos;
- opus_int64 read_ahead_thresh;
- int available;
- int just_read_ahead;
-
- read_ahead_thresh=OP_MAX(OP_READAHEAD_THRESH_MIN,
- stream->connect_rate*conn->read_rate>>11);
- available=op_http_conn_estimate_available(conn);
- conn_pos=conn->pos;
- end_pos=conn->end_pos;
- if(conn->next_pos>=0){
- OP_ASSERT(end_pos>=0);
- OP_ASSERT(conn->next_pos==end_pos);
- end_pos=conn->next_end;
- }
- OP_ASSERT(end_pos<0||conn_pos<=end_pos);
-
- just_read_ahead=conn_pos<=pos&&pos-conn_pos-available<=read_ahead_thresh
- &&(end_pos<0||pos<end_pos);
- if(just_read_ahead||pipeline&&end_pos>=0
- &&end_pos-conn_pos-available<=read_ahead_thresh){
-
- ret=op_http_conn_read_ahead(stream,conn,just_read_ahead,pos);
- if(OP_UNLIKELY(ret<0)){
-
- op_http_conn_close(stream,conn,pnext,1);
- conn=*pnext;
- continue;
- }
-
- *pnext=conn->next;
- conn->next=stream->lru_head;
- stream->lru_head=conn;
- stream->cur_conni=conn-stream->conns;
- return 0;
- }
- close_pnext=pnext;
- close_conn=conn;
- pnext=&conn->next;
- conn=conn->next;
- }
-
- if(stream->free_head==NULL){
-
- OP_ASSERT(close_conn!=NULL);
- OP_ASSERT(close_pnext!=NULL);
- op_http_conn_close(stream,close_conn,close_pnext,1);
- }
- OP_ASSERT(stream->free_head!=NULL);
- conn=stream->free_head;
-
- ret=op_http_conn_open_pos(stream,conn,pos,
- pipeline?OP_PIPELINE_CHUNK_SIZE:-1);
- if(OP_UNLIKELY(ret<0)){
- op_http_conn_close(stream,conn,&stream->lru_head,1);
- return -1;
- }
- return 0;
- }
- static opus_int64 op_http_stream_tell(void *_stream){
- OpusHTTPStream *stream;
- int ci;
- stream=(OpusHTTPStream *)_stream;
- ci=stream->cur_conni;
- return ci<0?stream->pos:stream->conns[ci].pos;
- }
- static int op_http_stream_close(void *_stream){
- OpusHTTPStream *stream;
- stream=(OpusHTTPStream *)_stream;
- if(OP_LIKELY(stream!=NULL)){
- op_http_stream_clear(stream);
- _ogg_free(stream);
- }
- return 0;
- }
- static const OpusFileCallbacks OP_HTTP_CALLBACKS={
- op_http_stream_read,
- op_http_stream_seek,
- op_http_stream_tell,
- op_http_stream_close
- };
- #endif
- void opus_server_info_init(OpusServerInfo *_info){
- _info->name=NULL;
- _info->description=NULL;
- _info->genre=NULL;
- _info->url=NULL;
- _info->server=NULL;
- _info->content_type=NULL;
- _info->bitrate_kbps=-1;
- _info->is_public=-1;
- _info->is_ssl=0;
- }
- void opus_server_info_clear(OpusServerInfo *_info){
- _ogg_free(_info->content_type);
- _ogg_free(_info->server);
- _ogg_free(_info->url);
- _ogg_free(_info->genre);
- _ogg_free(_info->description);
- _ogg_free(_info->name);
- }
- static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
- int _skip_certificate_check,const char *_proxy_host,unsigned _proxy_port,
- const char *_proxy_user,const char *_proxy_pass,OpusServerInfo *_info){
- const char *path;
-
- path=op_parse_file_url(_url);
- if(path!=NULL){
- char *unescaped_path;
- void *ret;
- unescaped_path=op_string_dup(path);
- if(OP_UNLIKELY(unescaped_path==NULL))return NULL;
- ret=op_fopen(_cb,op_unescape_url_component(unescaped_path),"rb");
- _ogg_free(unescaped_path);
- return ret;
- }
- #if defined(OP_ENABLE_HTTP)
-
- else{
- OpusHTTPStream *stream;
- int ret;
- stream=(OpusHTTPStream *)_ogg_malloc(sizeof(*stream));
- if(OP_UNLIKELY(stream==NULL))return NULL;
- op_http_stream_init(stream);
- ret=op_http_stream_open(stream,_url,_skip_certificate_check,
- _proxy_host,_proxy_port,_proxy_user,_proxy_pass,_info);
- if(OP_UNLIKELY(ret<0)){
- op_http_stream_clear(stream);
- _ogg_free(stream);
- return NULL;
- }
- *_cb=*&OP_HTTP_CALLBACKS;
- return stream;
- }
- #else
- (void)_skip_certificate_check;
- (void)_proxy_host;
- (void)_proxy_port;
- (void)_proxy_user;
- (void)_proxy_pass;
- (void)_info;
- return NULL;
- #endif
- }
- void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
- const char *_url,OpusServerInfo *_info,OpusServerInfo **_pinfo,va_list _ap){
- int skip_certificate_check;
- const char *proxy_host;
- opus_int32 proxy_port;
- const char *proxy_user;
- const char *proxy_pass;
- OpusServerInfo *pinfo;
- skip_certificate_check=0;
- proxy_host=NULL;
- proxy_port=8080;
- proxy_user=NULL;
- proxy_pass=NULL;
- pinfo=NULL;
- for(;;){
- ptrdiff_t request;
- request=va_arg(_ap,char *)-(char *)NULL;
-
- if(!request)break;
- switch(request){
- case OP_SSL_SKIP_CERTIFICATE_CHECK_REQUEST:{
- skip_certificate_check=!!va_arg(_ap,opus_int32);
- }break;
- case OP_HTTP_PROXY_HOST_REQUEST:{
- proxy_host=va_arg(_ap,const char *);
- }break;
- case OP_HTTP_PROXY_PORT_REQUEST:{
- proxy_port=va_arg(_ap,opus_int32);
- if(proxy_port<0||proxy_port>(opus_int32)65535)return NULL;
- }break;
- case OP_HTTP_PROXY_USER_REQUEST:{
- proxy_user=va_arg(_ap,const char *);
- }break;
- case OP_HTTP_PROXY_PASS_REQUEST:{
- proxy_pass=va_arg(_ap,const char *);
- }break;
- case OP_GET_SERVER_INFO_REQUEST:{
- pinfo=va_arg(_ap,OpusServerInfo *);
- }break;
-
- default:return NULL;
- }
- }
-
- *_pinfo=NULL;
- if(pinfo!=NULL){
- void *ret;
- opus_server_info_init(_info);
- ret=op_url_stream_create_impl(_cb,_url,skip_certificate_check,
- proxy_host,proxy_port,proxy_user,proxy_pass,_info);
- if(ret!=NULL)*_pinfo=pinfo;
- else opus_server_info_clear(_info);
- return ret;
- }
- return op_url_stream_create_impl(_cb,_url,skip_certificate_check,
- proxy_host,proxy_port,proxy_user,proxy_pass,NULL);
- }
- void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
- const char *_url,va_list _ap){
- OpusServerInfo info;
- OpusServerInfo *pinfo;
- void *ret;
- ret=op_url_stream_vcreate_impl(_cb,_url,&info,&pinfo,_ap);
- if(pinfo!=NULL)*pinfo=*&info;
- return ret;
- }
- void *op_url_stream_create(OpusFileCallbacks *_cb,
- const char *_url,...){
- va_list ap;
- void *ret;
- va_start(ap,_url);
- ret=op_url_stream_vcreate(_cb,_url,ap);
- va_end(ap);
- return ret;
- }
- OggOpusFile *op_vopen_url(const char *_url,int *_error,va_list _ap){
- OpusFileCallbacks cb;
- OggOpusFile *of;
- OpusServerInfo info;
- OpusServerInfo *pinfo;
- void *source;
- source=op_url_stream_vcreate_impl(&cb,_url,&info,&pinfo,_ap);
- if(OP_UNLIKELY(source==NULL)){
- OP_ASSERT(pinfo==NULL);
- if(_error!=NULL)*_error=OP_EFAULT;
- return NULL;
- }
- of=op_open_callbacks(source,&cb,NULL,0,_error);
- if(OP_UNLIKELY(of==NULL)){
- if(pinfo!=NULL)opus_server_info_clear(&info);
- (*cb.close)(source);
- }
- else if(pinfo!=NULL)*pinfo=*&info;
- return of;
- }
- OggOpusFile *op_open_url(const char *_url,int *_error,...){
- OggOpusFile *ret;
- va_list ap;
- va_start(ap,_error);
- ret=op_vopen_url(_url,_error,ap);
- va_end(ap);
- return ret;
- }
- OggOpusFile *op_vtest_url(const char *_url,int *_error,va_list _ap){
- OpusFileCallbacks cb;
- OggOpusFile *of;
- OpusServerInfo info;
- OpusServerInfo *pinfo;
- void *source;
- source=op_url_stream_vcreate_impl(&cb,_url,&info,&pinfo,_ap);
- if(OP_UNLIKELY(source==NULL)){
- OP_ASSERT(pinfo==NULL);
- if(_error!=NULL)*_error=OP_EFAULT;
- return NULL;
- }
- of=op_test_callbacks(source,&cb,NULL,0,_error);
- if(OP_UNLIKELY(of==NULL)){
- if(pinfo!=NULL)opus_server_info_clear(&info);
- (*cb.close)(source);
- }
- else if(pinfo!=NULL)*pinfo=*&info;
- return of;
- }
- OggOpusFile *op_test_url(const char *_url,int *_error,...){
- OggOpusFile *ret;
- va_list ap;
- va_start(ap,_error);
- ret=op_vtest_url(_url,_error,ap);
- va_end(ap);
- return ret;
- }
|