12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407 |
- /*
- http.c -- GoAhead HTTP engine
- This module implements an embedded HTTP/1.1 web server. It supports
- loadable URL handlers that define the nature of URL processing performed.
- Copyright (c) All Rights Reserved. See details at the end of the file.
- */
- /********************************* Includes ***********************************/
- #include "goahead.h"
- /********************************* Defines ************************************/
- #define WEBS_TIMEOUT (ME_GOAHEAD_LIMIT_TIMEOUT * 1000)
- #define PARSE_TIMEOUT (ME_GOAHEAD_LIMIT_PARSE_TIMEOUT * 1000)
- #define CHUNK_LOW 128 /* Low water mark for chunking */
- /************************************ Locals **********************************/
- static int websBackground; /* Run as a daemon */
- static int websDebug; /* Run in debug mode and defeat timeouts */
- static int defaultHttpPort; /* Default port number for http */
- static int defaultSslPort; /* Default port number for https */
- static int listens[WEBS_MAX_LISTEN]; /* Listen endpoints */;
- static int listenMax; /* Max entry in listens */
- static Webs **webs; /* Open connection list head */
- static WebsHash websMime; /* Set of mime types */
- static int websMax; /* List size */
- static char websHost[ME_MAX_IP]; /* Host name for the server */
- static char websIpAddr[ME_MAX_IP]; /* IP address for the server */
- static char *websHostUrl = NULL; /* URL to access server */
- static char *websIpAddrUrl = NULL; /* URL to access server */
- #define WEBS_ENCODE_HTML 0x1 /* Bit setting in charMatch[] */
- /*
- Character escape/descape matching codes. Generated by charGen.
- */
- static uchar charMatch[256] = {
- 0x00,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3e,0x3c,0x3c,0x3c,0x3c,0x3c,
- 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,
- 0x3c,0x0c,0x3f,0x28,0x2a,0x3c,0x2b,0x0f,0x0e,0x0e,0x0e,0x28,0x28,0x00,0x00,0x28,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x2a,0x3f,0x28,0x3f,0x2a,
- 0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3a,0x3e,0x3a,0x3e,0x00,
- 0x3e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x3e,0x3e,0x02,0x3c,
- 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,
- 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,
- 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,
- 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,
- 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,
- 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,
- 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,
- 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c
- };
- /*
- Addd entries to the MimeList as required for your content
- */
- static WebsMime websMimeList[] = {
- { "application/java", ".class" },
- { "application/java", ".jar" },
- { "text/html", ".asp" },
- { "text/html", ".htm" },
- { "text/html", ".html" },
- { "text/xml", ".xml" },
- { "image/gif", ".gif" },
- { "image/jpeg", ".jpg" },
- { "image/png", ".png" },
- { "image/vnd.microsoft.icon", ".ico" },
- { "text/css", ".css" },
- { "text/plain", ".txt" },
- { "application/x-javascript", ".js" },
- { "application/x-shockwave-flash", ".swf" },
- { "application/binary", ".exe" },
- { "application/compress", ".z" },
- { "application/gzip", ".gz" },
- { "application/octet-stream", ".bin" },
- { "application/oda", ".oda" },
- { "application/pdf", ".pdf" },
- { "application/postscript", ".ai" },
- { "application/postscript", ".eps" },
- { "application/postscript", ".ps" },
- { "application/rtf", ".rtf" },
- { "application/x-bcpio", ".bcpio" },
- { "application/x-cpio", ".cpio" },
- { "application/x-csh", ".csh" },
- { "application/x-dvi", ".dvi" },
- { "application/x-gtar", ".gtar" },
- { "application/x-hdf", ".hdf" },
- { "application/x-latex", ".latex" },
- { "application/x-mif", ".mif" },
- { "application/x-netcdf", ".nc" },
- { "application/x-netcdf", ".cdf" },
- { "application/x-ns-proxy-autoconfig", ".pac" },
- { "application/x-patch", ".patch" },
- { "application/x-sh", ".sh" },
- { "application/x-shar", ".shar" },
- { "application/x-sv4cpio", ".sv4cpio" },
- { "application/x-sv4crc", ".sv4crc" },
- { "application/x-tar", ".tar" },
- { "application/x-tgz", ".tgz" },
- { "application/x-tcl", ".tcl" },
- { "application/x-tex", ".tex" },
- { "application/x-texinfo", ".texinfo" },
- { "application/x-texinfo", ".texi" },
- { "application/x-troff", ".t" },
- { "application/x-troff", ".tr" },
- { "application/x-troff", ".roff" },
- { "application/x-troff-man", ".man" },
- { "application/x-troff-me", ".me" },
- { "application/x-troff-ms", ".ms" },
- { "application/x-ustar", ".ustar" },
- { "application/x-wais-source", ".src" },
- { "application/zip", ".zip" },
- { "audio/basic", ".au snd" },
- { "audio/x-aiff", ".aif" },
- { "audio/x-aiff", ".aiff" },
- { "audio/x-aiff", ".aifc" },
- { "audio/x-wav", ".wav" },
- { "audio/x-wav", ".ram" },
- { "image/ief", ".ief" },
- { "image/jpeg", ".jpeg" },
- { "image/jpeg", ".jpe" },
- { "image/tiff", ".tiff" },
- { "image/tiff", ".tif" },
- { "image/x-cmu-raster", ".ras" },
- { "image/x-portable-anymap", ".pnm" },
- { "image/x-portable-bitmap", ".pbm" },
- { "image/x-portable-graymap", ".pgm" },
- { "image/x-portable-pixmap", ".ppm" },
- { "image/x-rgb", ".rgb" },
- { "image/x-xbitmap", ".xbm" },
- { "image/x-xpixmap", ".xpm" },
- { "image/x-xwindowdump", ".xwd" },
- { "text/html", ".cfm" },
- { "text/html", ".shtm" },
- { "text/html", ".shtml" },
- { "text/richtext", ".rtx" },
- { "text/tab-separated-values", ".tsv" },
- { "text/x-setext", ".etx" },
- { "video/mpeg", ".mpeg" },
- { "video/mpeg", ".mpg" },
- { "video/mpeg", ".mpe" },
- { "video/quicktime", ".qt" },
- { "video/quicktime", ".mov" },
- { "video/mp4", ".mp4" },
- { "video/x-msvideo", ".avi" },
- { "video/x-sgi-movie", ".movie" },
- { NULL, NULL},
- };
- /*
- Standard HTTP error codes
- */
- static WebsError websErrors[] = {
- { 200, "OK" },
- { 201, "Created" },
- { 204, "No Content" },
- { 205, "Reset Content" },
- { 206, "Partial Content" },
- { 301, "Redirect" },
- { 302, "Redirect" },
- { 304, "Not Modified" },
- { 400, "Bad Request" },
- { 401, "Unauthorized" },
- { 402, "Payment required" },
- { 403, "Forbidden" },
- { 404, "Not Found" },
- { 405, "Access Denied" },
- { 406, "Not Acceptable" },
- { 408, "Request Timeout" },
- { 413, "Request too large" },
- { 500, "Internal Server Error" },
- { 501, "Not Implemented" },
- { 503, "Service Unavailable" },
- { 0, NULL }
- };
- #if ME_GOAHEAD_ACCESS_LOG && !ME_ROM
- static char accessLog[64] = "access.log"; /* Log filename */
- static int accessFd; /* Log file handle */
- #endif
- static WebsHash sessions = -1;
- static int sessionCount = 0;
- static int pruneId; /* Callback ID */
- /**************************** Forward Declarations ****************************/
- static void checkTimeout(void *arg, int id);
- static bool filterChunkData(Webs *wp);
- static int getTimeSinceMark(Webs *wp);
- static char *getToken(Webs *wp, char *delim);
- static void parseFirstLine(Webs *wp);
- static void parseHeaders(Webs *wp);
- static bool processContent(Webs *wp);
- static bool parseIncoming(Webs *wp);
- static void pruneSessions();
- static void freeSession(WebsSession *sp);
- static void freeSessions();
- static void readEvent(Webs *wp);
- static void reuseConn(Webs *wp);
- static void setFileLimits();
- static int setLocalHost();
- static void socketEvent(int sid, int mask, void *data);
- static void writeEvent(Webs *wp);
- #if ME_GOAHEAD_ACCESS_LOG
- static void logRequest(Webs *wp, int code);
- #endif
- /*********************************** Code *************************************/
- PUBLIC int websOpen(char *documents, char *routeFile)
- {
- WebsMime *mt;
- webs = NULL;
- websMax = 0;
- websOsOpen();
- websRuntimeOpen();
- websTimeOpen();
- websFsOpen();
- setFileLimits();
- socketOpen();
- if (setLocalHost() < 0) {
- return -1;
- }
- #if ME_COM_SSL
- if (sslOpen() < 0) {
- return -1;
- }
- #endif
- if ((sessions = hashCreate(-1)) < 0) {
- return -1;
- }
- if (!websDebug) {
- pruneId = websStartEvent(WEBS_SESSION_PRUNE, (WebsEventProc) pruneSessions, 0);
- }
- if (documents) {
- websSetDocuments(documents);
- }
- if (websOpenRoute() < 0) {
- return -1;
- }
- #if ME_GOAHEAD_CGI
- websCgiOpen();
- #endif
- websOptionsOpen();
- websActionOpen();
- websFileOpen();
- #if ME_GOAHEAD_UPLOAD
- websUploadOpen();
- #endif
- #if ME_GOAHEAD_JAVASCRIPT
- websJstOpen();
- #endif
- #if ME_GOAHEAD_AUTH
- if (websOpenAuth(0) < 0) {
- return -1;
- }
- #endif
- if (websLoad(routeFile) < 0) {
- return -1;
- }
- /*
- Create a mime type lookup table for quickly determining the content type
- */
- websMime = hashCreate(WEBS_HASH_INIT * 4);
- assert(websMime >= 0);
- for (mt = websMimeList; mt->type; mt++) {
- hashEnter(websMime, mt->ext, valueString(mt->type, 0), 0);
- }
- #if ME_GOAHEAD_ACCESS_LOG && !ME_ROM
- if ((accessFd = open(accessLog, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 0666)) < 0) {
- printf("Cannot open access log %s", accessLog);
- return -1;
- }
- /* Some platforms don't implement O_APPEND (VXWORKS) */
- lseek(accessFd, 0, SEEK_END);
- #endif
- return 0;
- }
- PUBLIC void websClose()
- {
- Webs *wp;
- int i;
- //printf("%s %d ---> websClose()\n", __FILE__, __LINE__);
- websCloseRoute();
- #if ME_GOAHEAD_AUTH
- websCloseAuth();
- #endif
- if (pruneId >= 0) {
- websStopEvent(pruneId);
- pruneId = -1;
- }
- if (sessions >= 0) {
- freeSessions();
- }
- for (i = 0; i < listenMax; i++) {
- if (listens[i] >= 0) {
- socketCloseConnection(listens[i]);
- listens[i] = -1;
- }
- }
- listenMax = 0;
- for (i = websMax; webs && i >= 0; i--) {
- if ((wp = webs[i]) == NULL) {
- continue;
- }
- if (wp->sid >= 0) {
- socketCloseConnection(wp->sid);
- wp->sid = -1;
- }
- websFree(wp);
- }
- wfree(websHostUrl);
- wfree(websIpAddrUrl);
- websIpAddrUrl = websHostUrl = NULL;
- #if ME_COM_SSL
- sslClose();
- #endif
- #if ME_GOAHEAD_ACCESS_LOG
- if (accessFd >= 0) {
- close(accessFd);
- accessFd = -1;
- }
- #endif
- websFsClose();
- hashFree(websMime);
- socketClose();
- websTimeClose();
- websRuntimeClose();
- websOsClose();
- }
- static void initWebs(Webs *wp, int flags, int reuse)
- {
- WebsBuf rxbuf;
- void *ssl;
- char ipaddr[ME_MAX_IP], ifaddr[ME_MAX_IP];
- int wid, sid, timeout;
- assert(wp);
- if (reuse) {
- rxbuf = wp->rxbuf;
- wid = wp->wid;
- sid = wp->sid;
- timeout = wp->timeout;
- ssl = wp->ssl;
- scopy(ipaddr, sizeof(ipaddr), wp->ipaddr);
- scopy(ifaddr, sizeof(ifaddr), wp->ifaddr);
- } else {
- wid = sid = -1;
- timeout = -1;
- ssl = 0;
- }
- memset(wp, 0, sizeof(Webs));
- wp->flags = flags;
- wp->state = WEBS_BEGIN;
- wp->wid = wid;
- wp->sid = sid;
- wp->timeout = timeout;
- wp->docfd = -1;
- wp->txLen = -1;
- wp->rxLen = -1;
- wp->code = HTTP_CODE_OK;
- wp->ssl = ssl;
- #if !ME_ROM
- wp->putfd = -1;
- #endif
- #if ME_GOAHEAD_CGI
- wp->cgifd = -1;
- #endif
- #if ME_GOAHEAD_UPLOAD
- wp->files = -1;
- wp->upfd = -1;
- #endif
- if (reuse) {
- scopy(wp->ipaddr, sizeof(wp->ipaddr), ipaddr);
- scopy(wp->ifaddr, sizeof(wp->ifaddr), ifaddr);
- } else {
- wp->timeout = -1;
- }
- wp->vars = hashCreate(WEBS_HASH_INIT);
- /*
- Ring queues can never be totally full and are short one byte. Better to do even I/O and allocate
- a little more memory than required. The chunkbuf has extra room to fit chunk headers and trailers.
- */
- assert(ME_GOAHEAD_LIMIT_BUFFER >= 1024);
- bufCreate(&wp->output, ME_GOAHEAD_LIMIT_BUFFER + 1, ME_GOAHEAD_LIMIT_BUFFER + 1);
- bufCreate(&wp->chunkbuf, ME_GOAHEAD_LIMIT_BUFFER + 1, ME_GOAHEAD_LIMIT_BUFFER * 2);
- bufCreate(&wp->input, ME_GOAHEAD_LIMIT_BUFFER + 1, ME_GOAHEAD_LIMIT_PUT + 1);
- if (reuse) {
- wp->rxbuf = rxbuf;
- } else {
- bufCreate(&wp->rxbuf, ME_GOAHEAD_LIMIT_HEADERS, ME_GOAHEAD_LIMIT_HEADERS + ME_GOAHEAD_LIMIT_PUT);
- }
- }
- static void termWebs(Webs *wp, int reuse)
- {
- assert(wp);
- /*
- Some of this is done elsewhere, but keep this here for when a shutdown is done and there are open connections.
- */
- bufFree(&wp->input);
- bufFree(&wp->output);
- bufFree(&wp->chunkbuf);
- if (!reuse) {
- bufFree(&wp->rxbuf);
- if (wp->sid >= 0) {
- #if ME_COM_SSL
- sslFree(wp);
- #endif
- socketDeleteHandler(wp->sid);
- socketCloseConnection(wp->sid);
- wp->sid = -1;
- }
- }
- #if !ME_ROM
- if (wp->putfd >= 0) {
- close(wp->putfd);
- wp->putfd = -1;
- assert(wp->putname && wp->filename);
- if (rename(wp->putname, wp->filename) < 0) {
- printf("Cannot rename PUT file from %s to %s", wp->putname, wp->filename);
- }
- }
- #endif
- #if ME_GOAHEAD_CGI
- if (wp->cgifd >= 0) {
- close(wp->cgifd);
- wp->cgifd = -1;
- }
- wfree(wp->cgiStdin);
- #endif
- #if ME_GOAHEAD_UPLOAD
- wfree(wp->clientFilename);
- #endif
- websPageClose(wp);
- if (wp->timeout >= 0 && !reuse) {
- websCancelTimeout(wp);
- }
- wfree(wp->authDetails);
- wfree(wp->authResponse);
- wfree(wp->authType);
- wfree(wp->contentType);
- wfree(wp->cookie);
- wfree(wp->decodedQuery);
- wfree(wp->digest);
- wfree(wp->ext);
- wfree(wp->filename);
- wfree(wp->host);
- wfree(wp->method);
- wfree(wp->password);
- wfree(wp->path);
- wfree(wp->protoVersion);
- wfree(wp->putname);
- wfree(wp->query);
- wfree(wp->realm);
- wfree(wp->referrer);
- wfree(wp->responseCookie);
- wfree(wp->url);
- wfree(wp->userAgent);
- wfree(wp->username);
- #if ME_GOAHEAD_UPLOAD
- wfree(wp->boundary);
- wfree(wp->uploadTmp);
- wfree(wp->uploadVar);
- #endif
- #if ME_GOAHEAD_DIGEST
- wfree(wp->cnonce);
- wfree(wp->digestUri);
- wfree(wp->opaque);
- wfree(wp->nc);
- wfree(wp->nonce);
- wfree(wp->qop);
- #endif
- hashFree(wp->vars);
- #if ME_GOAHEAD_UPLOAD
- if (wp->files >= 0) {
- websFreeUpload(wp);
- }
- #endif
- }
- PUBLIC int websAlloc(int sid)
- {
- Webs *wp;
- int wid;
- if ((wid = wallocObject(&webs, &websMax, sizeof(Webs))) < 0) {
- return -1;
- }
- wp = webs[wid];
- assert(wp);
- initWebs(wp, 0, 0);
- wp->wid = wid;
- wp->sid = sid;
- wp->timestamp = time(0);
- return wid;
- }
- static void reuseConn(Webs *wp)
- {
- assert(wp);
- assert(websValid(wp));
- bufCompact(&wp->rxbuf);
- if (bufLen(&wp->rxbuf)) {
- socketReservice(wp->sid);
- }
- termWebs(wp, 1);
- initWebs(wp, wp->flags & (WEBS_KEEP_ALIVE | WEBS_SECURE | WEBS_HTTP11), 1);
- }
- PUBLIC void websFree(Webs *wp)
- {
- assert(wp);
- assert(websValid(wp));
- termWebs(wp, 0);
- websMax = wfreeHandle(&webs, wp->wid);
- wfree(wp);
- assert(websMax >= 0);
- }
- /*
- Called when the request is complete. Note: it may not have fully drained from the tx buffer.
- */
- PUBLIC void websDone(Webs *wp)
- {
- WebsSocket *sp;
- assert(wp);
- assert(websValid(wp));
- if (wp->finalized) {
- return;
- }
- assert(WEBS_BEGIN <= wp->state && wp->state <= WEBS_COMPLETE);
- #if DEPRECATED || 1
- wp->flags |= WEBS_FINALIZED;
- #endif
- wp->finalized = 1;
- if (wp->state < WEBS_COMPLETE) {
- /*
- Initiate flush. If not all flushed, wait for output to drain via a socket event.
- */
- if (websFlush(wp, 0) == 0) {
- sp = socketPtr(wp->sid);
- socketCreateHandler(wp->sid, sp->handlerMask | SOCKET_WRITABLE, socketEvent, wp);
- }
- }
- #if ME_GOAHEAD_ACCESS_LOG
- logRequest(wp, wp->code);
- #endif
- if (!(wp->flags & WEBS_RESPONSE_TRACED)) {
- printf("Request complete: code %d", wp->code);
- }
- }
- static int complete(Webs *wp, int reuse)
- {
- assert(wp);
- assert(websValid(wp));
- assert(wp->state == WEBS_BEGIN || wp->state == WEBS_COMPLETE);
- if (reuse && wp->flags & WEBS_KEEP_ALIVE && wp->rxRemaining == 0) {
- reuseConn(wp);
- socketCreateHandler(wp->sid, SOCKET_READABLE, socketEvent, wp);
- //printf( "Keep connection alive");
- return 1;
- }
- //printf("Close connection");
- wp->state = WEBS_BEGIN;
- wp->flags |= WEBS_CLOSED;
- return 0;
- }
- PUBLIC int websListen(char *endpoint)
- {
- WebsSocket *sp;
- char *ip, *ipaddr;
- int port, secure, sid;
- assert(endpoint && *endpoint);
- if (listenMax >= WEBS_MAX_LISTEN) {
- printf("Too many listen endpoints");
- return -1;
- }
- socketParseAddress(endpoint, &ip, &port, &secure, 80);
- if ((sid = socketListen(ip, port, websAccept, 0)) < 0) {
- printf("Unable to open socket on port %d.", port);
- return -1;
- }
- sp = socketPtr(sid);
- sp->secure = secure;
- if (sp->secure) {
- if (!defaultSslPort) {
- defaultSslPort = port;
- }
- } else if (!defaultHttpPort) {
- defaultHttpPort = port;
- }
- listens[listenMax++] = sid;
- if (ip) {
- ipaddr = smatch(ip, "::") ? "[::]" : ip;
- } else {
- ipaddr = "*";
- }
- //printf("Started %s://%s:%d", secure ? "https" : "http", ipaddr, port);
- if (!websHostUrl) {
- if (port == 80) {
- websHostUrl = sclone(ip ? ip : websIpAddr);
- } else {
- websHostUrl = sfmt("%s:%d", ip ? ip : websIpAddr, port);
- }
- }
- if (!websIpAddrUrl) {
- if (port == 80) {
- websIpAddrUrl = sclone(websIpAddr);
- } else {
- websIpAddrUrl = sfmt("%s:%d", websIpAddr, port);
- }
- }
- wfree(ip);
- return sid;
- }
- /*
- Accept a new connection from ipaddr:port
- */
- PUBLIC int websAccept(int sid, char *ipaddr, int port, int listenSid)
- {
- Webs *wp;
- WebsSocket *lp;
- struct sockaddr_storage ifAddr;
- int wid, len;
- assert(sid >= 0);
- assert(ipaddr && *ipaddr);
- assert(listenSid >= 0);
- assert(port >= 0);
- /*
- Allocate a new handle for this accepted connection. This will allocate a Webs structure in the webs[] list
- */
- if ((wid = websAlloc(sid)) < 0) {
- return -1;
- }
- wp = webs[wid];
- assert(wp);
- wp->listenSid = listenSid;
- strncpy(wp->ipaddr, ipaddr, min(sizeof(wp->ipaddr) - 1, strlen(ipaddr)));
- /*
- Get the ip address of the interface that accept the connection.
- */
- len = sizeof(ifAddr);
- if (getsockname(socketPtr(sid)->sock, (struct sockaddr*) &ifAddr, (Socklen*) &len) < 0) {
- printf("Cannot get sockname");
- return -1;
- }
- socketAddress((struct sockaddr*) &ifAddr, (int) len, wp->ifaddr, sizeof(wp->ifaddr), NULL);
- #if ME_GOAHEAD_LEGACY
- /*
- Check if this is a request from a browser on this system. This is useful to know for permitting administrative
- operations only for local access
- */
- if (strcmp(wp->ipaddr, "127.0.0.1") == 0 || strcmp(wp->ipaddr, websIpAddr) == 0 ||
- strcmp(wp->ipaddr, websHost) == 0) {
- wp->flags |= WEBS_LOCAL;
- }
- #endif
- /*
- Arrange for socketEvent to be called when read data is available
- */
- lp = socketPtr(listenSid);
- //printf( "New connection from %s:%d to %s:%d", ipaddr, port, wp->ifaddr, lp->port);
- #if ME_COM_SSL
- if (lp->secure) {
- wp->flags |= WEBS_SECURE;
- //printf( "Upgrade connection to TLS");
- if (sslUpgrade(wp) < 0) {
- printf("Cannot upgrade to TLS");
- return -1;
- }
- }
- #endif
- assert(wp->timeout == -1);
- wp->timeout = websStartEvent(PARSE_TIMEOUT, checkTimeout, (void*) wp);
- socketEvent(sid, SOCKET_READABLE, wp);
- return 0;
- }
- /*
- The webs socket handler. Called in response to I/O. We just pass control to the relevant read or write handler. A
- pointer to the webs structure is passed as a (void*) in wptr.
- */
- static void socketEvent(int sid, int mask, void *wptr)
- {
- Webs *wp;
- wp = (Webs*) wptr;
- assert(wp);
- assert(websValid(wp));
- if (! websValid(wp)) {
- return;
- }
- if (mask & SOCKET_READABLE) {
- readEvent(wp);
- }
- if (mask & SOCKET_WRITABLE) {
- writeEvent(wp);
- }
- if (wp->flags & WEBS_CLOSED) {
- websFree(wp);
- /* WARNING: wp not valid here */
- }
- }
- /*
- Read from a connection. Return the number of bytes read if successful. This may be less than the requested "len" and
- may be zero. Return -1 for errors or EOF. Distinguish between error and EOF via socketEof().
- */
- static ssize websRead(Webs *wp, char *buf, ssize len)
- {
- assert(wp);
- assert(buf);
- assert(len > 0);
- #if ME_COM_SSL
- if (wp->flags & WEBS_SECURE) {
- return sslRead(wp, buf, len);
- }
- #endif
- return socketRead(wp->sid, buf, len);
- }
- /*
- The webs read handler. This is the primary read event loop. It uses a state machine to track progress while parsing
- the HTTP request. Note: we never block as the socket is always in non-blocking mode.
- */
- static void readEvent(Webs *wp)
- {
- WebsBuf *rxbuf;
- WebsSocket *sp;
- ssize nbytes;
- assert(wp);
- assert(websValid(wp));
- if (!websValid(wp)) {
- return;
- }
- websNoteRequestActivity(wp);
- rxbuf = &wp->rxbuf; //存储的是请求包中的所有数据 18432 //缓冲区的数据结构
- // printf("%d>>>>>>>>0000>>>>%d>>>\n",rxbuf->buflen, bufRoom(rxbuf));
- if (bufRoom(rxbuf) < (ME_GOAHEAD_LIMIT_BUFFER+1)) { //缓冲区不够了增加缓冲区的大小
- if (!bufGrow(rxbuf, (ME_GOAHEAD_LIMIT_BUFFER+1))) {
- websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot grow rxbuf");
- websPump(wp);
- printf("jimbo exit goahead\n");
- exit(0); //jimbo add. 强制退出goahead,然后让damon.sh脚本重启goahead。
- return;
- }
- }
- //printf(">>>rxbuf->endp: %p\n",rxbuf->endp);
- // ME_GOAHEAD_LIMIT_BUFFER
- if ((nbytes = websRead(wp, (char*) rxbuf->endp, ME_GOAHEAD_LIMIT_BUFFER)) > 0) {//调用socketRead,读HTTP请求.rxbuf->endp是上一次的数据尾,每次读之后接上
- // printf("%d>>>>>>>>2222>>>>>>>\n",nbytes);//一次读了多少字节
- wp->lastRead = nbytes;//读了多少字节,数据的尾指针就加多少字节
- // printf("---> websRead return %d\n",nBytes);
- bufAdjustEnd(rxbuf, nbytes);//写字符串结束符
- bufAddNull(rxbuf);
- }
- // printf("%d>>>>>>>>56565656>>>>>>>\n",nbytes);
- if (nbytes > 0 || wp->state > WEBS_BEGIN) { //读到数据了,进来处理
- // printf("%d>>>>>>>>2222>>>>>>>\n",nbytes);
- websPump(wp);
- }
- //到这一步nbytes值是-1 为什么??
- // printf("%d>>>>>>>>>nbytes>>>>>>>>>>>\n",nbytes);
- // printf("%d>>>>>>>>>wp->sid>>>>>>>>>>>\n",wp->sid);
- if (wp->flags & WEBS_CLOSED) {
- return;//通过websPump处理完请求,需要关闭连接,return返回readEvent.数据结构依然保留。
- } else if (nbytes < 0 && socketEof(wp->sid)) {
- /* EOF or error. Allow running requests to continue. */
- // printf("%d>>>>>>>>>state>>>>>>>>>>>\n", wp->state);
- //state 值是1
- if (wp->state < WEBS_READY) {//ready 2
- if (wp->state > WEBS_BEGIN) {//begin 0
- websError(wp, HTTP_CODE_COMMS_ERROR, "Read error: connection lost");
- websPump(wp);
- } else {
- complete(wp, 0);
- }
- } else {
- socketDeleteHandler(wp->sid);
- }
- } else if (wp->state < WEBS_READY) {//如果是keep alive的请求,继续监听。
- sp = socketPtr(wp->sid);
- socketCreateHandler(wp->sid, sp->handlerMask | SOCKET_READABLE, socketEvent, wp);
- }
- }
- PUBLIC void websPump(Webs *wp)
- {
- bool canProceed;
- for (canProceed = 1; canProceed; ) {
- switch (wp->state) {
- case WEBS_BEGIN:
- canProceed = parseIncoming(wp);
- break;
- case WEBS_CONTENT:
- canProceed = processContent(wp);
- break;
- case WEBS_READY:
- if (!websRunRequest(wp)) {
- // printf("%d<<<<<<<<<<<<>>>>>>>>>*******\n", WEBS_READY);
- /* Reroute if the handler re-wrote the request */
- websRouteRequest(wp);
- wp->state = WEBS_READY;
- canProceed = 1;
- continue;
- }
- canProceed = (wp->state != WEBS_RUNNING);
- break;
- case WEBS_RUNNING:
- /* Nothing to do until websDone is called */
- return;
- case WEBS_COMPLETE:
- canProceed = complete(wp, 1);
- break;
- }
- }
- }
- static bool parseIncoming(Webs *wp)
- {
- WebsBuf *rxbuf;
- char *end, c;
- rxbuf = &wp->rxbuf;
- while (*rxbuf->servp == '\r' || *rxbuf->servp == '\n') {
- if (bufGetc(rxbuf) < 0) {
- break;
- }
- }
- if ((end = strstr((char*) wp->rxbuf.servp, "\r\n\r\n")) == 0) {
- if (bufLen(&wp->rxbuf) >= ME_GOAHEAD_LIMIT_HEADER) {
- websError(wp, HTTP_CODE_REQUEST_TOO_LARGE | WEBS_CLOSE, "Header too large");
- return 1;
- }
- return 0;
- }
- //printf("\n<<< Request\n");
- c = *end;
- *end = '\0';
- //printf( "%s\n", wp->rxbuf.servp);
- *end = c;
- /*
- Parse the first line of the Http header
- */
- parseFirstLine(wp);
- if (wp->state == WEBS_COMPLETE) {
- return 1;
- }
- parseHeaders(wp);
- if (wp->state == WEBS_COMPLETE) {
- return 1;
- }
- wp->state = (wp->rxChunkState || wp->rxLen > 0) ? WEBS_CONTENT : WEBS_READY;
- websRouteRequest(wp);
- if (wp->state == WEBS_COMPLETE) {
- return 1;
- }
- #if ME_GOAHEAD_CGI
- if (wp->route && wp->route->handler && wp->route->handler->service == cgiHandler) {
- if (smatch(wp->method, "POST")) {
- wp->cgiStdin = websGetCgiCommName();
- if ((wp->cgifd = open(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY | O_TRUNC, 0666)) < 0) {
- websError(wp, HTTP_CODE_NOT_FOUND | WEBS_CLOSE, "Cannot open CGI file");
- return 1;
- }
- }
- }
- #endif
- #if !ME_ROM
- if (smatch(wp->method, "PUT")) {
- WebsStat sbuf;
- wp->code = (stat(wp->filename, &sbuf) == 0 && sbuf.st_mode & S_IFDIR) ? HTTP_CODE_NO_CONTENT : HTTP_CODE_CREATED;
- wfree(wp->putname);
- wp->putname = websTempFile(ME_GOAHEAD_PUT_DIR, "put");
- if ((wp->putfd = open(wp->putname, O_BINARY | O_WRONLY | O_CREAT | O_BINARY, 0644)) < 0) {
- printf("Cannot create PUT filename %s", wp->putname);
- websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot create the put URI");
- wfree(wp->putname);
- return 1;
- }
- }
- #endif
- return 1;
- }
- /*
- Parse the first line of a HTTP request
- */
- static void parseFirstLine(Webs *wp)
- {
- char *op, *protoVer, *url, *host, *query, *path, *port, *ext, *buf;
- int listenPort;
- assert(wp);
- assert(websValid(wp));
- /*
- Determine the request type: GET, HEAD or POST
- */
- op = getToken(wp, 0);
- if (op == NULL || *op == '\0') {
- websError(wp, HTTP_CODE_NOT_FOUND | WEBS_CLOSE, "Bad HTTP request");
- return;
- }
- wp->method = supper(sclone(op));
- url = getToken(wp, 0);
- if (url == NULL || *url == '\0') {
- websError(wp, HTTP_CODE_BAD_REQUEST | WEBS_CLOSE, "Bad HTTP request");
- return;
- }
- if (strlen(url) > ME_GOAHEAD_LIMIT_URI) {
- websError(wp, HTTP_CODE_REQUEST_URL_TOO_LARGE | WEBS_CLOSE, "URI too big");
- return;
- }
- protoVer = getToken(wp, "\r\n");
- // if (websGetLogLevel() == 2) {
- // printf("===> %s %s %s", wp->method, url, protoVer);
- // }
- /*
- Parse the URL and store all the various URL components. websUrlParse returns an allocated buffer in buf which we
- must free. We support both proxied and non-proxied requests. Proxied requests will have http://host/ at the
- start of the URL. Non-proxied will just be local path names.
- */
- host = path = port = query = ext = NULL;
- if (websUrlParse(url, &buf, NULL, &host, &port, &path, &ext, NULL, &query) < 0) {
- printf("Cannot parse URL: %s", url);
- websError(wp, HTTP_CODE_BAD_REQUEST | WEBS_CLOSE | WEBS_NOLOG, "Bad URL");
- return;
- }
- if ((wp->path = websValidateUriPath(path)) == 0) {
- websError(wp, HTTP_CODE_BAD_REQUEST | WEBS_CLOSE | WEBS_NOLOG, "Bad URL");
- wfree(buf);
- return;
- }
- wp->url = sclone(url);
- if (ext) {
- wp->ext = sclone(slower(ext));
- }
- wp->filename = sfmt("%s%s", websGetDocuments(), wp->path);
- wp->query = sclone(query);
- wp->host = sclone(host);
- wp->protocol = wp->flags & WEBS_SECURE ? "https" : "http";
- if (smatch(protoVer, "HTTP/1.1")) {
- wp->flags |= WEBS_KEEP_ALIVE | WEBS_HTTP11;
- } else if (smatch(protoVer, "HTTP/1.0")) {
- wp->flags &= ~(WEBS_HTTP11);
- } else {
- protoVer = "HTTP/1.1";
- websError(wp, WEBS_CLOSE | HTTP_CODE_NOT_ACCEPTABLE, "Unsupported HTTP protocol");
- }
- wp->protoVersion = sclone(protoVer);
- if ((listenPort = socketGetPort(wp->listenSid)) >= 0) {
- wp->port = listenPort;
- } else {
- wp->port = atoi(port);
- }
- wfree(buf);
- }
- /*
- Parse a full request
- */
- static void parseHeaders(Webs *wp)
- {
- char *combined, *prior, *upperKey, *cp, *key, *value, *tok;
- int count;
- assert(websValid(wp));
- /*
- Parse the header and create the Http header keyword variables
- We rewrite the header as we go for non-local requests. NOTE: this
- modifies the header string directly and tokenizes each line with '\0'.
- */
- for (count = 0; wp->rxbuf.servp[0] != '\r'; count++) {
- if (count >= ME_GOAHEAD_LIMIT_NUM_HEADERS) {
- websError(wp, HTTP_CODE_REQUEST_TOO_LARGE | WEBS_CLOSE, "Too many headers");
- return;
- }
- if ((key = getToken(wp, ":")) == NULL) {
- continue;
- }
- if ((value = getToken(wp, "\r\n")) == NULL) {
- value = "";
- }
- if (!key || !value) {
- websError(wp, HTTP_CODE_BAD_REQUEST | WEBS_CLOSE, "Bad header format");
- return;
- }
- while (isspace((uchar) *value)) {
- value++;
- }
- slower(key);
- /*
- Create a header variable for each line in the header
- */
- upperKey = sfmt("HTTP_%s", key);
- for (cp = upperKey; *cp; cp++) {
- if (*cp == '-') {
- *cp = '_';
- }
- }
- supper(upperKey);
- if ((prior = websGetVar(wp, upperKey, 0)) != 0) {
- combined = sfmt("%s, %s", prior, value);
- websSetVar(wp, upperKey, combined);
- wfree(combined);
- } else {
- websSetVar(wp, upperKey, value);
- }
- wfree(upperKey);
- /*
- Track the requesting agent (browser) type
- */
- if (strcmp(key, "user-agent") == 0) {
- wfree(wp->userAgent);
- wp->userAgent = sclone(value);
- } else if (scaselesscmp(key, "authorization") == 0) {
- wfree(wp->authType);
- wp->authType = sclone(value);
- ssplit(wp->authType, " \t", &tok);
- wfree(wp->authDetails);
- wp->authDetails = sclone(tok);
- slower(wp->authType);
- } else if (strcmp(key, "connection") == 0) {
- slower(value);
- if (strcmp(value, "keep-alive") == 0) {
- wp->flags |= WEBS_KEEP_ALIVE;
- } else if (strcmp(value, "close") == 0) {
- wp->flags &= ~WEBS_KEEP_ALIVE;
- }
- } else if (strcmp(key, "content-length") == 0) {
- wp->rxLen = atoi(value);
- if (smatch(wp->method, "PUT")) {
- if (wp->rxLen > ME_GOAHEAD_LIMIT_PUT) {
- websError(wp, HTTP_CODE_REQUEST_TOO_LARGE | WEBS_CLOSE, "Too big");
- return;
- }
- } else {
- // if (wp->rxLen > ME_GOAHEAD_LIMIT_POST) {
- // websError(wp, HTTP_CODE_REQUEST_TOO_LARGE | WEBS_CLOSE, "Too big");
- // return;
- // }
- }
- if (wp->rxLen > 0 && !smatch(wp->method, "HEAD")) {
- wp->rxRemaining = wp->rxLen;
- }
- } else if (strcmp(key, "content-type") == 0) {
- wfree(wp->contentType);
- wp->contentType = sclone(value);
- // printf("%s>>>>>>>>>>>>>>>wp->contentType>>>>>\n", wp->contentType);
- if (strstr(value, "application/x-www-form-urlencoded")) {
- wp->flags |= WEBS_FORM;
- } else if (strstr(value, "application/json")) {
- wp->flags |= WEBS_JSON;
- } else if (strstr(value, "multipart/form-data")) {
- wp->flags |= WEBS_UPLOAD;
- }
- } else if (strcmp(key, "cookie") == 0) {
- wp->flags |= WEBS_COOKIE;
- if (wp->cookie) {
- char *prior = wp->cookie;
- wp->cookie = sfmt("%s; %s", prior, value);
- wfree(prior);
- } else {
- wp->cookie = sclone(value);
- }
- } else if (strcmp(key, "host") == 0) {
- if ((int) strspn(value, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.[]:")
- < (int) slen(value)) {
- websError(wp, WEBS_CLOSE | HTTP_CODE_BAD_REQUEST, "Bad host header");
- return;
- }
- wfree(wp->host);
- wp->host = sclone(value);
- } else if (strcmp(key, "if-modified-since") == 0) {
- if ((cp = strchr(value, ';')) != NULL) {
- *cp = '\0';
- }
- websParseDateTime(&wp->since, value, 0);
- /*
- Yes Veronica, the HTTP spec does misspell Referrer
- */
- } else if (strcmp(key, "referer") == 0) {
- wfree(wp->referrer);
- wp->referrer = sclone(value);
- } else if (strcmp(key, "transfer-encoding") == 0) {
- if (scaselesscmp(value, "chunked") == 0) {
- wp->rxChunkState = WEBS_CHUNK_START;
- wp->rxRemaining = MAXINT;
- }
- }
- }
- if (!wp->rxChunkState) {
- /*
- Step over "\r\n" after headers.
- Don't do this if chunked so that chunking can parse a single chunk delimiter of "\r\nSIZE ...\r\n"
- */
- assert(bufLen(&wp->rxbuf) >= 2);
- wp->rxbuf.servp += 2;
- }
- wp->eof = (wp->rxRemaining == 0);
- }
- static bool processContent(Webs *wp)
- {
- bool canProceed;
- canProceed = filterChunkData(wp);
- if (!canProceed || wp->finalized) {
- return canProceed;
- }
- #if ME_GOAHEAD_UPLOAD
- if (wp->flags & WEBS_UPLOAD) {
- // printf("<>>>>>>>>>tttttttttttt>>>>>>>>>\n");
- canProceed = websProcessUploadData(wp);
- if (!canProceed || wp->finalized) {
- return canProceed;
- }
- }
- #endif
- #if !ME_ROM
- if (wp->putfd >= 0) {
- canProceed = websProcessPutData(wp);
- if (!canProceed || wp->finalized) {
- return canProceed;
- }
- }
- #endif
- #if ME_GOAHEAD_CGI
- if (wp->cgifd >= 0) {
- canProceed = websProcessCgiData(wp);
- if (!canProceed || wp->finalized) {
- return canProceed;
- }
- }
- #endif
- if (wp->eof) {
- wp->state = WEBS_READY;
- /*
- Prevent reading content from the next request
- The handler may not have been created if all the content was read in the initial read. No matter.
- */
- socketDeleteHandler(wp->sid);
- }
- return canProceed;
- }
- /*
- Always called when data is consumed from the input buffer
- */
- PUBLIC void websConsumeInput(Webs *wp, ssize nbytes)
- {
- assert(wp);
- assert(nbytes >= 0);
- assert(bufLen(&wp->input) >= nbytes);
- if (nbytes <= 0) {
- return;
- }
- bufAdjustStart(&wp->input, nbytes);
- if (bufLen(&wp->input) == 0) {
- bufReset(&wp->input);
- }
- }
- static bool filterChunkData(Webs *wp)
- {
- WebsBuf *rxbuf;
- ssize chunkSize;
- char *start, *cp;
- ssize len, nbytes;
- int bad;
- assert(wp);
- assert(wp->rxbuf.buf);
- rxbuf = &wp->rxbuf;
- while (bufLen(rxbuf) > 0) {
- switch (wp->rxChunkState) {
- case WEBS_CHUNK_UNCHUNKED:
- len = min(wp->rxRemaining, bufLen(rxbuf));
- // printf("---> bufPutBlk1 len = %d\n", len);
- bufPutBlk(&wp->input, rxbuf->servp, len);
- bufAddNull(&wp->input);
- bufAdjustStart(rxbuf, len);
- bufCompact(rxbuf);
- wp->rxRemaining -= len;
- if (wp->rxRemaining <= 0) {
- wp->eof = 1;
- }
- assert(wp->rxRemaining >= 0);
- return 1;
- case WEBS_CHUNK_START:
- /*
- Expect: "\r\nSIZE.*\r\n"
- */
- if (bufLen(rxbuf) < 5) {
- return 0;
- }
- start = rxbuf->servp;
- bad = (start[0] != '\r' || start[1] != '\n');
- for (cp = &start[2]; cp < rxbuf->endp && *cp != '\n'; cp++) {}
- if (*cp != '\n' && (cp - start) < 80) {
- /* Insufficient data */
- return 0;
- }
- bad += (cp[-1] != '\r' || cp[0] != '\n');
- if (bad) {
- websError(wp, WEBS_CLOSE | HTTP_CODE_BAD_REQUEST, "Bad chunk specification");
- return 1;
- }
- chunkSize = hextoi(&start[2]);
- if (!isxdigit((uchar) start[2]) || chunkSize < 0) {
- websError(wp, WEBS_CLOSE | HTTP_CODE_BAD_REQUEST, "Bad chunk specification");
- return 1;
- }
- if (chunkSize == 0) {
- /* On the last chunk, consume the final "\r\n" */
- if ((cp + 2) >= rxbuf->endp) {
- /* Insufficient data */
- return 0;
- }
- cp += 2;
- bad += (cp[-1] != '\r' || cp[0] != '\n');
- if (bad) {
- websError(wp, WEBS_CLOSE | HTTP_CODE_BAD_REQUEST, "Bad final chunk specification");
- return 1;
- }
- }
- bufAdjustStart(rxbuf, cp - start + 1);
- wp->rxChunkSize = chunkSize;
- wp->rxRemaining = chunkSize;
- if (chunkSize == 0) {
- #if ME_GOAHEAD_LEGACY
- wfree(wp->query);
- wp->query = sclone(bufStart(&wp->input));
- #endif
- wp->eof = 1;
- return 1;
- }
- printf( "chunkFilter: start incoming chunk of %d bytes", chunkSize);
- wp->rxChunkState = WEBS_CHUNK_DATA;
- break;
- case WEBS_CHUNK_DATA:
- len = min(bufLen(rxbuf), wp->rxRemaining);
- nbytes = min(bufRoom(&wp->input), len);
- // printf("---> bufPutBlk2 len = %d\n", len);
- if (len > 0 && (nbytes = bufPutBlk(&wp->input, rxbuf->servp, nbytes)) == 0) {
- //printf("<<<<<<<<<<<<<<>>42222>>>>>>>>>>>>\n" );
- websError(wp, HTTP_CODE_REQUEST_TOO_LARGE | WEBS_CLOSE, "Too big");
- return 1;
- }
- bufAddNull(&wp->input);
- bufAdjustStart(rxbuf, nbytes);
- wp->rxRemaining -= nbytes;
- if (wp->rxRemaining <= 0) {
- wp->rxChunkState = WEBS_CHUNK_START;
- bufCompact(rxbuf);
- }
- break;
- }
- }
- return 0;
- }
- /*
- Basic event loop. SocketReady returns true when a socket is ready for service. SocketSelect will block until an
- event occurs. SocketProcess will actually do the servicing.
- */
- PUBLIC void websServiceEvents(int *finished)
- {
- int delay, nextEvent;
- if (finished) {
- *finished = 0;
- }
- delay = 0;
- while (!finished || !*finished) {
- if (socketSelect(-1, delay)) {
- socketProcess();
- }
- #if ME_GOAHEAD_CGI
- delay = websCgiPoll();
- #else
- delay = MAXINT;
- #endif
- nextEvent = websRunEvents();
- delay = min(delay, nextEvent);
- }
- }
- /*
- NOTE: the vars variable is modified
- */
- static void addFormVars(Webs *wp, char *vars)
- {
- WebsKey *sp;
- char *keyword, *value, *prior, *tok;
- assert(wp);
- assert(vars);
- keyword = stok(vars, "&", &tok);
- while (keyword != NULL) {
- if ((value = strchr(keyword, '=')) != NULL) {
- *value++ = '\0';
- websDecodeUrl(keyword, keyword, strlen(keyword));
- websDecodeUrl(value, value, strlen(value));
- } else {
- value = "";
- }
- if (*keyword) {
- /*
- If keyword has already been set, append the new value to what has been stored.
- */
- if ((prior = websGetVar(wp, keyword, NULL)) != 0) {
- sp = websSetVarFmt(wp, keyword, "%s %s", prior, value);
- } else {
- sp = websSetVar(wp, keyword, value);
- }
- /* Flag as untrusted keyword by setting arg to 1. This is used by CGI to prefix this keyword */
- sp->arg = 1;
- }
- keyword = stok(NULL, "&", &tok);
- }
- }
- /*
- Set the variable (CGI) environment for this request. Create variables for all standard CGI variables. Also decode
- the query string and create a variable for each name=value pair.
- */
- PUBLIC void websSetEnv(Webs *wp)
- {
- assert(wp);
- assert(websValid(wp));
- websSetVar(wp, "AUTH_TYPE", wp->authType);
- websSetVarFmt(wp, "CONTENT_LENGTH", "%d", wp->rxLen);
- websSetVar(wp, "CONTENT_TYPE", wp->contentType);
- if (wp->route && wp->route->dir) {
- websSetVar(wp, "DOCUMENT_ROOT", wp->route->dir);
- }
- websSetVar(wp, "GATEWAY_INTERFACE", "CGI/1.1");
- websSetVar(wp, "PATH_INFO", wp->path);
- websSetVar(wp, "PATH_TRANSLATED", wp->filename);
- websSetVar(wp, "QUERY_STRING", wp->query);
- websSetVar(wp, "REMOTE_ADDR", wp->ipaddr);
- websSetVar(wp, "REMOTE_USER", wp->username);
- websSetVar(wp, "REMOTE_HOST", wp->ipaddr);
- websSetVar(wp, "REQUEST_METHOD", wp->method);
- websSetVar(wp, "REQUEST_TRANSPORT", wp->protocol);
- websSetVar(wp, "REQUEST_URI", wp->path);
- websSetVar(wp, "SERVER_ADDR", wp->ifaddr);
- websSetVar(wp, "SERVER_HOST", websHost);
- websSetVar(wp, "SERVER_NAME", websHost);
- websSetVarFmt(wp, "SERVER_PORT", "%d", wp->port);
- websSetVar(wp, "SERVER_PROTOCOL", wp->protoVersion);
- websSetVar(wp, "SERVER_URL", websHostUrl);
- websSetVarFmt(wp, "SERVER_SOFTWARE", "GoAhead/%s", ME_VERSION);
- }
- PUBLIC void websSetFormVars(Webs *wp)
- {
- char *data;
- if (wp->rxLen > 0 && bufLen(&wp->input) > 0) {
- if (wp->flags & WEBS_FORM) {
- data = sclone(wp->input.servp);
- addFormVars(wp, data);
- wfree(data);
- }
- }
- }
- PUBLIC void websSetQueryVars(Webs *wp)
- {
- /*
- Decode and create an environment query variable for each query keyword. We split into pairs at each '&', then
- split pairs at the '='. Note: we rely on wp->decodedQuery preserving the decoded values in the symbol table.
- */
- if (wp->query && *wp->query) {
- wfree(wp->decodedQuery);
- wp->decodedQuery = sclone(wp->query);
- addFormVars(wp, wp->decodedQuery);
- }
- }
- /*
- Define a webs (CGI) variable for this connection. Also create in relevant scripting engines. Note: the incoming
- value may be volatile.
- */
- PUBLIC WebsKey *websSetVarFmt(Webs *wp, char *var, char *fmt, ...)
- {
- WebsValue v;
- va_list args;
- assert(websValid(wp));
- assert(var && *var);
- if (fmt) {
- va_start(args, fmt);
- v = valueString(sfmtv(fmt, args), 0);
- v.allocated = 1;
- va_end(args);
- } else {
- v = valueString("", 0);
- }
- return hashEnter(wp->vars, var, v, 0);
- }
- PUBLIC WebsKey *websSetVar(Webs *wp, char *var, char *value)
- {
- WebsValue v;
- assert(websValid(wp));
- assert(var && *var);
- if (value) {
- v = valueString(value, VALUE_ALLOCATE);
- } else {
- v = valueString("", 0);
- }
- return hashEnter(wp->vars, var, v, 0);
- }
- /*
- Return TRUE if a webs variable exists for this connection.
- */
- PUBLIC bool websTestVar(Webs *wp, char *var)
- {
- WebsKey *sp;
- assert(websValid(wp));
- assert(var && *var);
- if (var == NULL || *var == '\0') {
- return 0;
- }
- if ((sp = hashLookup(wp->vars, var)) == NULL) {
- return 0;
- }
- return 1;
- }
- /*
- Get a webs variable but return a default value if string not found. Note, defaultGetValue can be NULL to permit
- testing existence.
- */
- PUBLIC char *websGetVar(Webs *wp, char *var, char *defaultGetValue)
- {
- WebsKey *sp;
- assert(websValid(wp));
- assert(var && *var);
- if ((sp = hashLookup(wp->vars, var)) != NULL) {
- assert(sp->content.type == string);
- if (sp->content.value.string) {
- return sp->content.value.string;
- } else {
- return "";
- }
- }
- return defaultGetValue;
- }
- /*
- Return TRUE if a webs variable is set to a given value
- */
- PUBLIC int websCompareVar(Webs *wp, char *var, char *value)
- {
- assert(websValid(wp));
- assert(var && *var);
- if (strcmp(value, websGetVar(wp, var, " __UNDEF__ ")) == 0) {
- return 1;
- }
- return 0;
- }
- /*
- Cancel the request timeout. Note may be called multiple times.
- */
- PUBLIC void websCancelTimeout(Webs *wp)
- {
- assert(websValid(wp));
- if (wp->timeout >= 0) {
- websStopEvent(wp->timeout);
- wp->timeout = -1;
- }
- }
- /*
- Output a HTTP response back to the browser. If redirect is set to a URL, the browser will be sent to this location.
- */
- PUBLIC void websResponse(Webs *wp, int code, char *message)
- {
- ssize len;
- assert(websValid(wp));
- websSetStatus(wp, code);
- if (!smatch(wp->method, "HEAD") && message && *message) {
- len = slen(message);
- websWriteHeaders(wp, len + 2, 0);
- websWriteEndHeaders(wp);
- websWriteBlock(wp, message, len);
- websWriteBlock(wp, "\r\n", 2);
- } else {
- websWriteHeaders(wp, 0, 0);
- websWriteEndHeaders(wp);
- }
- websDone(wp);
- }
- static char *makeUri(char *scheme, char *host, int port, char *path)
- {
- if (port <= 0) {
- port = smatch(scheme, "https") ? defaultSslPort : defaultHttpPort;
- }
- if (port == 80 || port == 443) {
- return sfmt("%s://%s%s", scheme, host, path);
- }
- return sfmt("%s://%s:%d%s", scheme, host, port, path);
- }
- /*
- Redirect the user to another webs page
- */
- PUBLIC void websRedirect(Webs *wp, char *uri)
- {
- char *message, *location, *scheme, *host, *pstr;
- char hostbuf[ME_GOAHEAD_LIMIT_STRING];
- bool secure, fullyQualified;
- ssize len;
- int originalPort, port;
- assert(websValid(wp));
- assert(uri);
- message = location = NULL;
- originalPort = port = 0;
- if ((host = (wp->host ? wp->host : websHostUrl)) != 0) {
- scopy(hostbuf, sizeof(hostbuf), host);
- pstr = strchr(hostbuf, ']');
- pstr = pstr ? pstr : hostbuf;
- if ((pstr = strchr(pstr, ':')) != 0) {
- *pstr++ = '\0';
- originalPort = atoi(pstr);
- }
- }
- //printf("---> originalPort: %d, hostbuf: %s\n", originalPort, hostbuf);
- if (smatch(uri, "http://") || smatch(uri, "https://")) {
- /* Protocol switch with existing Uri */
- scheme = sncmp(uri, "https", 5) == 0 ? "https" : "http";
- uri = location = makeUri(scheme, hostbuf, 0, wp->url);
- }
- secure = strstr(uri, "https://") != 0;
- fullyQualified = strstr(uri, "http://") || strstr(uri, "https://");
- if (!fullyQualified) {
- port = originalPort;
- if (wp->flags & WEBS_SECURE) {
- secure = 1;
- }
- }
- scheme = secure ? "https" : "http";
- if (port <= 0) {
- port = secure ? defaultSslPort : defaultHttpPort;
- }
- if (strstr(uri, "https:///")) {
- /* Short-hand for redirect to https */
- uri = location = makeUri(scheme, hostbuf, port, &uri[8]);
- } else if (strstr(uri, "http:///")) {
- uri = location = makeUri(scheme, hostbuf, port, &uri[7]);
- } else if (!fullyQualified) {
- uri = location = makeUri(scheme, hostbuf, port, uri);
- }
- message = sfmt("<html><head></head><body>\r\n\
- This document has moved to a new <a href=\"%s\">location</a>.\r\n\
- Please update your documents to reflect the new location.\r\n\
- </body></html>\r\n", uri);
- len = slen(message);
- websSetStatus(wp, HTTP_CODE_MOVED_TEMPORARILY);
- websWriteHeaders(wp, len + 2, uri);
- websWriteEndHeaders(wp);
- websWriteBlock(wp, message, len);
- websWriteBlock(wp, "\r\n", 2);
- websDone(wp);
- wfree(message);
- wfree(location);
- }
- PUBLIC int websRedirectByStatus(Webs *wp, int status)
- {
- WebsKey *key;
- char code[16], *uri;
- assert(wp);
- assert(status >= 0);
- if (wp->route && wp->route->redirects >= 0) {
- itosbuf(code, sizeof(code), status, 10);
- if ((key = hashLookup(wp->route->redirects, code)) != 0) {
- uri = key->content.value.string;
- } else {
- return -1;
- }
- websRedirect(wp, uri);
- } else {
- if (status == HTTP_CODE_UNAUTHORIZED) {
- websError(wp, status, "Access Denied. User not logged in.");
- } else {
- websError(wp, status, 0);
- }
- }
- return 0;
- }
- /*
- Escape HTML to escape defined characters (prevent cross-site scripting)
- Returns an allocated string.
- */
- PUBLIC char *websEscapeHtml(char *html)
- {
- char *ip, *result, *op;
- int len;
- if (!html) {
- return sclone("");
- }
- for (len = 1, ip = html; *ip; ip++, len++) {
- if (charMatch[(int) (uchar) *ip] & WEBS_ENCODE_HTML) {
- len += 5;
- }
- }
- if ((result = walloc(len)) == 0) {
- return 0;
- }
- /*
- Leave room for the biggest expansion
- */
- op = result;
- while (*html != '\0') {
- if (charMatch[(uchar) *html] & WEBS_ENCODE_HTML) {
- if (*html == '&') {
- strcpy(op, "&");
- op += 5;
- } else if (*html == '<') {
- strcpy(op, "<");
- op += 4;
- } else if (*html == '>') {
- strcpy(op, ">");
- op += 4;
- } else if (*html == '#') {
- strcpy(op, "#");
- op += 5;
- } else if (*html == '(') {
- strcpy(op, "(");
- op += 5;
- } else if (*html == ')') {
- strcpy(op, ")");
- op += 5;
- } else if (*html == '"') {
- strcpy(op, """);
- op += 6;
- } else if (*html == '\'') {
- strcpy(op, "'");
- op += 5;
- } else {
- assert(0);
- }
- html++;
- } else {
- *op++ = *html++;
- }
- }
- assert(op < &result[len]);
- *op = '\0';
- return result;
- }
- PUBLIC int websWriteHeader(Webs *wp, char *key, char *fmt, ...)
- {
- va_list vargs;
- char *buf;
- assert(websValid(wp));
- if (!(wp->flags & WEBS_RESPONSE_TRACED)) {
- wp->flags |= WEBS_RESPONSE_TRACED;
- // printf( "\n>>> Response\n");
- }
- if (key) {
- if (websWriteBlock(wp, key, strlen(key)) < 0) {
- return -1;
- }
- if (websWriteBlock(wp, ": ", 2) < 0) {
- return -1;
- }
- //printf("===> %s: ", key);
- }
- if (fmt) {
- va_start(vargs, fmt);
- if ((buf = sfmtv(fmt, vargs)) == 0) {
- printf("websWrite lost data, buffer overflow");
- return -1;
- }
- va_end(vargs);
- assert(strstr(buf, "UNION") == 0);
- //printf("===> %s", buf);
- if (websWriteBlock(wp, buf, strlen(buf)) < 0) {
- return -1;
- }
- wfree(buf);
- if (websWriteBlock(wp, "\r\n", 2) != 2) {
- return -1;
- }
- }
- //printf("\r\n");
- return 0;
- }
- PUBLIC void websSetStatus(Webs *wp, int code)
- {
- wp->code = (code & WEBS_CODE_MASK);
- if (code & WEBS_CLOSE) {
- wp->flags &= ~WEBS_KEEP_ALIVE;
- }
- }
- /*
- Write a set of headers. Does not write the trailing blank line so callers can add more headers.
- Set length to -1 if unknown and transfer-chunk-encoding will be employed.
- */
- PUBLIC void websWriteHeaders(Webs *wp, ssize length, char *location)
- {
- WebsKey *key;
- char *date, *protoVersion;
- assert(websValid(wp));
- if (!(wp->flags & WEBS_HEADERS_CREATED)) {
- protoVersion = wp->protoVersion;
- if (!protoVersion) {
- protoVersion = "HTTP/1.0";
- wp->flags &= ~WEBS_KEEP_ALIVE;
- }
- websWriteHeader(wp, NULL, "%s %d %s", protoVersion, wp->code, websErrorMsg(wp->code));
- #if !ME_GOAHEAD_STEALTH
- websWriteHeader(wp, "Server", "GoAhead-http");
- #endif
- if ((date = websGetDateString(NULL)) != NULL) {
- websWriteHeader(wp, "Date", "%s", date);
- wfree(date);
- }
- if (wp->authResponse) {
- websWriteHeader(wp, "WWW-Authenticate", "%s", wp->authResponse);
- }
- if (length >= 0) {
- if (smatch(wp->method, "HEAD")) {
- websWriteHeader(wp, "Content-Length", "%d", (int) length);
- } else if (!((100 <= wp->code && wp->code <= 199) || wp->code == 204 || wp->code == 304)) {
- /* Server must not emit a content length header for 1XX, 204 and 304 status */
- websWriteHeader(wp, "Content-Length", "%d", (int) length);
- }
- }
- wp->txLen = length;
- if (wp->txLen < 0) {
- websWriteHeader(wp, "Transfer-Encoding", "chunked");
- }
- if (wp->flags & WEBS_KEEP_ALIVE) {
- websWriteHeader(wp, "Connection", "keep-alive");
- } else {
- websWriteHeader(wp, "Connection", "close");
- }
- if (location) {
- websWriteHeader(wp, "Location", "%s", location);
- } else if ((key = hashLookup(websMime, wp->ext)) != 0) {
- websWriteHeader(wp, "Content-Type", "%s", key->content.value.string);
- }
- if (wp->responseCookie) {
- websWriteHeader(wp, "Set-Cookie", "%s", wp->responseCookie);
- websWriteHeader(wp, "Cache-Control", "%s", "no-cache=\"set-cookie\"");
- }
- #if defined(ME_GOAHEAD_CLIENT_CACHE)
- if (wp->ext) {
- char *etok = sfmt("%s,", &wp->ext[1]);
- if (strstr(ME_GOAHEAD_CLIENT_CACHE ",", etok)) {
- websWriteHeader(wp, "Cache-Control", "public, max-age=%d", ME_GOAHEAD_CLIENT_CACHE_LIFESPAN);
- }
- wfree(etok);
- }
- #endif
- #ifdef ME_GOAHEAD_XFRAME_HEADER
- if (*ME_GOAHEAD_XFRAME_HEADER) {
- websWriteHeader(wp, "X-Frame-Options", "%s", ME_GOAHEAD_XFRAME_HEADER);
- }
- #endif
- }
- }
- PUBLIC void websWriteEndHeaders(Webs *wp)
- {
- assert(wp);
- /*
- By omitting the "\r\n" delimiter after the headers, chunks can emit "\r\nSize\r\n" as a single chunk delimiter
- */
- if (wp->txLen >= 0) {
- websWriteBlock(wp, "\r\n", 2);
- }
- wp->flags |= WEBS_HEADERS_CREATED;
- if (wp->txLen < 0) {
- wp->flags |= WEBS_CHUNKING;
- }
- }
- PUBLIC void websSetTxLength(Webs *wp, ssize length)
- {
- assert(wp);
- wp->txLen = length;
- }
- /*
- Do formatted output to the browser. This is the public Javascript and form write procedure.
- */
- PUBLIC ssize websWrite(Webs *wp, char *fmt, ...)
- {
- va_list vargs;
- char *buf;
- ssize rc;
- //assert(websValid(wp));
- //assert(fmt && *fmt);
- va_start(vargs, fmt);
- buf = NULL;
- rc = 0;
- if ((buf = sfmtv(fmt, vargs)) == 0) {
- printf("websWrite lost data, buffer overflow");
- }
- va_end(vargs);
- //assert(buf);
- if (buf) {
- rc = websWriteBlock(wp, buf, strlen(buf));
- wfree(buf);
- }
- return rc;
- }
- /*
- Non-blocking write to socket.
- Returns number of bytes written. Returns -1 on errors. May return short.
- */
- PUBLIC ssize websWriteSocket(Webs *wp, char *buf, ssize size)
- {
- ssize written;
- assert(wp);
- assert(buf);
- assert(size >= 0);
- if (wp->flags & WEBS_CLOSED) {
- return -1;
- }
- #if ME_COM_SSL
- if (wp->flags & WEBS_SECURE) {
- if ((written = sslWrite(wp, buf, size)) < 0) {
- return written;
- }
- } else
- #endif
- if ((written = socketWrite(wp->sid, buf, size)) < 0) {
- return written;
- }
- wp->written += written;
- websNoteRequestActivity(wp);
- return written;
- }
- /*
- Write some output using transfer chunk encoding if required.
- Returns true if all the data was written. Otherwise return zero.
- */
- static bool flushChunkData(Webs *wp)
- {
- ssize len, written, room;
- assert(wp);
- while (bufLen(&wp->chunkbuf) > 0) {
- /*
- Stop if there is not room for a reasonable size chunk.
- Subtract 16 to allow for the final trailer.
- */
- if ((room = bufRoom(&wp->output) - 16) <= CHUNK_LOW) {
- bufGrow(&wp->output, CHUNK_LOW - room + 1);
- if ((room = bufRoom(&wp->output) - 16) <= CHUNK_LOW) {
- return 0;
- }
- }
- switch (wp->txChunkState) {
- default:
- case WEBS_CHUNK_START:
- /* Select the chunk size so that both the prefix and data will fit */
- wp->txChunkLen = min(bufLen(&wp->chunkbuf), room - 16);
- fmt(wp->txChunkPrefix, sizeof(wp->txChunkPrefix), "\r\n%x\r\n", wp->txChunkLen);
- wp->txChunkPrefixLen = slen(wp->txChunkPrefix);
- wp->txChunkPrefixNext = wp->txChunkPrefix;
- wp->txChunkState = WEBS_CHUNK_HEADER;
- break;
- case WEBS_CHUNK_HEADER:
- // printf("---> bufPutBlk3 len = %d\n", wp->txChunkPrefixLen);
- if ((written = bufPutBlk(&wp->output, wp->txChunkPrefixNext, wp->txChunkPrefixLen)) < 0) {
- return 0;
- } else {
- wp->txChunkPrefixNext += written;
- wp->txChunkPrefixLen -= written;
- if (wp->txChunkPrefixLen <= 0) {
- wp->txChunkState = WEBS_CHUNK_DATA;
- } else {
- return 0;
- }
- }
- break;
- case WEBS_CHUNK_DATA:
- if (wp->txChunkLen > 0) {
- len = min(room, wp->txChunkLen);
- // printf("---> bufPutBlk4 len = %d, room = %d, wp->txChunkLen = %d\n", len, room, wp->txChunkLen);
- if ((written = bufPutBlk(&wp->output, wp->chunkbuf.servp, len)) != len) {
- assert(0);
- return -1;
- }
- bufAdjustStart(&wp->chunkbuf, written);
- wp->txChunkLen -= written;
- if (wp->txChunkLen <= 0) {
- wp->txChunkState = WEBS_CHUNK_START;
- bufCompact(&wp->chunkbuf);
- }
- bufAddNull(&wp->output);
- }
- }
- }
- return bufLen(&wp->chunkbuf) == 0;
- }
- /*
- Initiate flushing output buffer. Returns true if all data is written to the socket and the buffer is empty.
- Returns < 0 for errors
- == 0 if there is output remaining to be flushed
- == 1 if the output was fully written to the socket
- */
- PUBLIC int websFlush(Webs *wp, bool block)
- {
- WebsBuf *op;
- ssize nbytes, written;
- int errCode, wasBlocking;
- if (block) {
- wasBlocking = socketSetBlock(wp->sid, 1);
- }
- op = &wp->output;
- if (wp->flags & WEBS_CHUNKING) {
- //printf("websFlush chunking finalized %d", wp->finalized);
- if (flushChunkData(wp) && wp->finalized) {
- //printf("websFlush: write chunk trailer");
- bufPutStr(op, "\r\n0\r\n\r\n");
- bufAddNull(op);
- wp->flags &= ~WEBS_CHUNKING;
- }
- }
- // printf( "websFlush: buflen %d", bufLen(op));
- written = 0;
- while ((nbytes = bufLen(op)) > 0) {
- if ((written = websWriteSocket(wp, op->servp, nbytes)) < 0) {
- errCode = socketGetError();
- if (errCode == EWOULDBLOCK || errCode == EAGAIN) {
- /* Not an error */
- written = 0;
- break;
- }
- /*
- Connection Error
- */
- wp->flags &= ~WEBS_KEEP_ALIVE;
- bufFlush(op);
- wp->state = WEBS_COMPLETE;
- break;
- } else if (written == 0) {
- break;
- }
- //printf( "websFlush: wrote %d to socket", written);
- bufAdjustStart(op, written);
- bufCompact(op);
- nbytes = bufLen(op);
- }
- assert(websValid(wp));
- if (bufLen(op) == 0 && wp->finalized) {
- wp->state = WEBS_COMPLETE;
- }
- if (block) {
- socketSetBlock(wp->sid, wasBlocking);
- }
- if (written < 0) {
- /* I/O Error */
- return -1;
- }
- return bufLen(op) == 0;
- }
- /*
- Respond to a writable event. First write any tx buffer by calling websFlush.
- Then write body data if writeProc is defined. If all written, ensure transition to complete state.
- Calls websPump() to advance state.
- */
- static void writeEvent(Webs *wp)
- {
- WebsBuf *op;
- op = &wp->output;
- if (bufLen(op) > 0) {
- websFlush(wp, 0);
- }
- if (bufLen(op) == 0 && wp->writeData) {
- (wp->writeData)(wp);
- }
- if (wp->state != WEBS_RUNNING) {
- websPump(wp);
- }
- }
- PUBLIC void websSetBackgroundWriter(Webs *wp, WebsWriteProc proc)
- {
- WebsSocket *sp;
- WebsBuf *op;
- assert(proc);
- wp->writeData = proc;
- op = &wp->output;
- if (bufLen(op) > 0) {
- websFlush(wp, 0);
- }
- if (bufLen(op) == 0) {
- (wp->writeData)(wp);
- }
- if (wp->sid >= 0 && wp->state < WEBS_COMPLETE) {
- sp = socketPtr(wp->sid);
- socketCreateHandler(wp->sid, sp->handlerMask | SOCKET_WRITABLE, socketEvent, wp);
- }
- }
- /*
- Write a block of data of length to the user's browser. Output is buffered and flushed via websFlush.
- This routine will never return "short". i.e. it will return the requested size to write or -1.
- Buffer data. Will flush as required. May return -1 on write errors.
- */
- PUBLIC ssize websWriteBlock(Webs *wp, char *buf, ssize size)
- {
- WebsBuf *op;
- ssize written, thisWrite, len, room;
- assert(wp);
- assert(websValid(wp));
- assert(buf);
- assert(size >= 0);
- if (wp->state >= WEBS_COMPLETE) {
- return -1;
- }
- op = (wp->flags & WEBS_CHUNKING) ? &wp->chunkbuf : &wp->output;
- written = len = 0;
- while (size > 0 && wp->state < WEBS_COMPLETE) {
- if (bufRoom(op) < size) {
- /*
- This will do a blocking I/O write. Will only ever fail for I/O errors.
- */
- if (websFlush(wp, 1) < 0) {
- return -1;
- }
- }
- if ((room = bufRoom(op)) == 0) {
- break;
- }
- thisWrite = min(room, size);
- // printf("---> bufPutBlk5 room = %d, size = %d, thisWrite = %d, buf = %s\n", room, size, thisWrite, buf);
- bufPutBlk(op, buf, thisWrite);
- size -= thisWrite;
- buf += thisWrite;
- written += thisWrite;
- }
- bufAddNull(op);
- if (wp->state >= WEBS_COMPLETE && written == 0) {
- return -1;
- }
- return written;
- }
- /*
- Decode a URL (or part thereof). Allows insitu decoding.
- */
- PUBLIC void websDecodeUrl(char *decoded, char *input, ssize len)
- {
- char *ip, *op;
- int num, i, c;
- assert(decoded);
- assert(input);
- if (len < 0) {
- len = strlen(input);
- }
- op = decoded;
- for (ip = input; *ip && len > 0; ip++, op++) {
- if (*ip == '+') {
- *op = ' ';
- } else if (*ip == '%' && isxdigit((uchar) ip[1]) && isxdigit((uchar) ip[2])) {
- /*
- Convert %nn to a single character
- */
- ip++;
- for (i = 0, num = 0; i < 2; i++, ip++) {
- c = tolower((uchar) *ip);
- if (c >= 'a' && c <= 'f') {
- num = (num * 16) + 10 + c - 'a';
- } else {
- num = (num * 16) + c - '0';
- }
- }
- *op = (char) num;
- ip--;
- } else {
- *op = *ip;
- }
- len--;
- }
- *op = '\0';
- }
- #if ME_GOAHEAD_ACCESS_LOG && !ME_ROM
- /*
- Output a log message in Common Log Format: See http://httpd.apache.org/docs/1.3/logs.html#common
- */
- static void logRequest(Webs *wp, int code)
- {
- char *buf, timeStr[28], zoneStr[6], dataStr[16];
- ssize len;
- WebsTime timer;
- struct tm localt;
- #if WINDOWS
- DWORD dwRet;
- TIME_ZONE_INFORMATION tzi;
- #endif
- assert(wp);
- time(&timer);
- #if WINDOWS
- localtime_s(&localt, &timer);
- #else
- localtime_r(&timer, &localt);
- #endif
- strftime(timeStr, sizeof(timeStr), "%d/%b/%Y:%H:%M:%S", &localt);
- timeStr[sizeof(timeStr) - 1] = '\0';
- #if WINDOWS
- dwRet = GetTimeZoneInformation(&tzi);
- fmt(zoneStr, sizeof(zoneStr), "%+03d00", -(int) (tzi.Bias/60));
- #elif !VXWORKS
- fmt(zoneStr, sizeof(zoneStr), "%+03d00", (int) (localt.tm_gmtoff/3600));
- #else
- zoneStr[0] = '\0';
- #endif
- zoneStr[sizeof(zoneStr) - 1] = '\0';
- if (wp->written != 0) {
- fmt(dataStr, sizeof(dataStr), "%Ld", wp->written);
- dataStr[sizeof(dataStr) - 1] = '\0';
- } else {
- dataStr[0] = '-'; dataStr[1] = '\0';
- }
- buf = NULL;
- buf = sfmt("%s - %s [%s %s] \"%s %s %s\" %d %s\n",
- wp->ipaddr, wp->username == NULL ? "-" : wp->username,
- timeStr, zoneStr, wp->method, wp->path, wp->protoVersion, code, dataStr);
- len = strlen(buf);
- write(accessFd, buf, len);
- wfree(buf);
- }
- #endif
- /*
- Request and connection timeout. The timeout triggers if we have not read any data from the
- users browser in the last WEBS_TIMEOUT period. If we have heard from the browser, simply
- re-issue the timeout.
- */
- static void checkTimeout(void *arg, int id)
- {
- Webs *wp;
- int elapsed, delay;
- wp = (Webs*) arg;
- assert(websValid(wp));
- elapsed = getTimeSinceMark(wp) * 1000;
- if (websDebug) {
- websRestartEvent(id, (int) WEBS_TIMEOUT);
- return;
- }
- if (wp->state == WEBS_BEGIN) {
- complete(wp, 0);
- websFree(wp);
- return;
- }
- if (elapsed >= WEBS_TIMEOUT) {
- if (!(wp->flags & WEBS_HEADERS_CREATED)) {
- if (wp->state > WEBS_BEGIN) {
- websError(wp, HTTP_CODE_REQUEST_TIMEOUT, "Request exceeded timeout");
- } else {
- websError(wp, HTTP_CODE_REQUEST_TIMEOUT, "Idle connection closed");
- }
- }
- wp->state = WEBS_COMPLETE;
- complete(wp, 0);
- websFree(wp);
- /* WARNING: wp not valid here */
- return;
- }
- delay = WEBS_TIMEOUT - elapsed;
- assert(delay > 0);
- websRestartEvent(id, delay);
- }
- static int get_local_ip(const char *eth_inf, char *ip)
- {
- int sd;
- struct sockaddr_in sin;
- struct ifreq ifr;
- sd = socket(AF_INET, SOCK_DGRAM, 0);
- if (-1 == sd)
- {
- printf("socket error: %s\n", strerror(errno));
- return -1;
- }
- strncpy(ifr.ifr_name, eth_inf, IFNAMSIZ);
- ifr.ifr_name[IFNAMSIZ - 1] = 0;
- // if error: No such device
- if (ioctl(sd, SIOCGIFADDR, &ifr) < 0)
- {
- printf("ioctl error: %s\n", strerror(errno));
- close(sd);
- return -1;
- }
- memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
- snprintf(ip, 16, "%s", inet_ntoa(sin.sin_addr));
- close(sd);
- return 0;
- }
- static int setLocalHost()
- {
- char ipaddr[16];
- get_local_ip("eth0", ipaddr);
- websSetIpAddr(ipaddr);
- websSetHost(ipaddr);
- //printf("---> setLocalHost, ip = %s\n", ipaddr);
- return 0;
- }
- PUBLIC void websSetHost(char *host)
- {
- scopy(websHost, sizeof(websHost), host);
- }
- PUBLIC void websSetHostUrl(char *url)
- {
- assert(url && *url);
- wfree(websHostUrl);
- websHostUrl = sclone(url);
- }
- PUBLIC void websSetIpAddr(char *ipaddr)
- {
- assert(ipaddr && *ipaddr);
- scopy(websIpAddr, sizeof(websIpAddr), ipaddr);
- }
- #if ME_GOAHEAD_LEGACY
- PUBLIC void websSetRequestFilename(Webs *wp, char *filename)
- {
- assert(websValid(wp));
- assert(filename && *filename);
- wfree(wp->filename);
- wp->filename = sclone(filename);
- websSetVar(wp, "PATH_TRANSLATED", wp->filename);
- }
- #endif
- PUBLIC int websRewriteRequest(Webs *wp, char *url)
- {
- char *buf, *path;
- wfree(wp->url);
- wp->url = sclone(url);
- wfree(wp->path);
- wp->path = 0;
- if (websUrlParse(url, &buf, NULL, NULL, NULL, &path, NULL, NULL, NULL) < 0) {
- return -1;
- }
- wp->path = sclone(path);
- wfree(wp->filename);
- wp->filename = 0;
- wp->flags |= WEBS_REROUTE;
- wfree(buf);
- return 0;
- }
- PUBLIC bool websValid(Webs *wp)
- {
- int wid;
- for (wid = 0; wid < websMax; wid++) {
- if (wp == webs[wid]) {
- return 1;
- }
- }
- return 0;
- }
- /*
- Build an ASCII time string. If sbuf is NULL we use the current time, else we use the last modified time of sbuf;
- */
- PUBLIC char *websGetDateString(WebsFileInfo *sbuf)
- {
- WebsTime now;
- struct tm tm;
- char *cp;
- if (sbuf == NULL) {
- time(&now);
- } else {
- now = sbuf->mtime;
- }
- #if ME_UNIX_LIKE
- gmtime_r(&now, &tm);
- #else
- {
- struct tm *tp;
- tp = gmtime(&now);
- tm = *tp;
- }
- #endif
- if ((cp = asctime(&tm)) != NULL) {
- cp[strlen(cp) - 1] = '\0';
- return sclone(cp);
- }
- return NULL;
- }
- /*
- Take not of the request activity and mark the time. Set a timestamp so that, later, we can return the number of seconds
- since we made the mark.
- */
- PUBLIC void websNoteRequestActivity(Webs *wp)
- {
- wp->timestamp = time(0);
- }
- /*
- Get the number of seconds since the last mark.
- */
- static int getTimeSinceMark(Webs *wp)
- {
- return (int) (time(0) - wp->timestamp);
- }
- PUBLIC bool websValidUriChars(char *uri)
- {
- ssize pos;
- if (uri == 0 || *uri == 0) {
- return 1;
- }
- pos = strspn(uri, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=%");
- if (pos < slen(uri)) {
- printf("Bad character in URI at \"%s\"", &uri[pos]);
- return 0;
- }
- return 1;
- }
- /*
- Parse the URL. A single buffer is allocated to store the parsed URL in *pbuf. This must be freed by the caller.
- */
- PUBLIC int websUrlParse(char *url, char **pbuf, char **pscheme, char **phost, char **pport, char **ppath, char **pext,
- char **preference, char **pquery)
- {
- char *tok, *delim, *host, *path, *port, *scheme, *reference, *query, *ext, *buf, *buf2;
- ssize buflen, ulen, len;
- int sep;
- assert(pbuf);
- if (url == 0) {
- url = "";
- }
- /*
- Allocate twice. Need to null terminate the host so have to copy the path.
- */
- ulen = strlen(url);
- len = ulen + 1;
- buflen = len * 2;
- if ((buf = walloc(buflen)) == NULL) {
- return -1;
- }
- buf2 = &buf[ulen + 1];
- sncopy(buf, len, url, ulen);
- sncopy(buf2, len, url, ulen);
- url = buf;
- scheme = 0;
- host = 0;
- port = 0;
- path = 0;
- ext = 0;
- query = 0;
- reference = 0;
- tok = buf;
- sep = '/';
- /*
- [scheme://][hostname[:port]][/path[.ext]][#ref][?query]
- First trim query and then reference from the end
- */
- if ((query = strchr(tok, '?')) != NULL) {
- *query++ = '\0';
- }
- if ((reference = strchr(tok, '#')) != NULL) {
- *reference++ = '\0';
- }
- /*
- [scheme://][hostname[:port]][/path]
- */
- if ((delim = strstr(tok, "://")) != 0) {
- scheme = tok;
- *delim = '\0';
- tok = &delim[3];
- }
- /*
- [hostname[:port]][/path]
- */
- if (*tok == '[' && ((delim = strchr(tok, ']')) != 0)) {
- /* IPv6 [::] */
- host = &tok[1];
- *delim++ = '\0';
- tok = delim;
- } else if (*tok && *tok != '/' && *tok != ':' && (scheme || strchr(tok, ':'))) {
- /*
- Supported forms:
- scheme://hostname
- hostname[:port][/path]
- */
- host = tok;
- if ((tok = strpbrk(tok, ":/")) == 0) {
- tok = "";
- }
- /* Don't terminate the hostname yet, need to see if tok is a ':' for a port. */
- assert(tok);
- }
- /* [:port][/path] */
- if (*tok == ':') {
- /* Terminate hostname */
- *tok++ = '\0';
- port = tok;
- if ((tok = strchr(tok, '/')) == 0) {
- tok = "";
- }
- }
- /* [/path] */
- if (*tok) {
- /*
- Terminate hostname. This zeros the leading path slash.
- This will be repaired before returning if ppath is set
- */
- sep = *tok;
- *tok++ = '\0';
- path = tok;
- /* path[.ext[/extra]] */
- if ((tok = strrchr(path, '.')) != 0) {
- if (tok[1]) {
- if ((delim = strrchr(path, '/')) != 0) {
- if (delim < tok) {
- ext = tok;
- }
- } else {
- ext = tok;
- }
- }
- }
- }
- /*
- Pass back the requested fields
- */
- *pbuf = buf;
- if (pscheme) {
- if (scheme == 0) {
- scheme = "http";
- }
- *pscheme = scheme;
- }
- if (phost) {
- if (host == 0) {
- host = "localhost";
- }
- *phost = host;
- }
- if (pport) {
- *pport = port;
- }
- if (ppath) {
- if (path == 0) {
- scopy(buf2, 1, "/");
- path = buf2;
- } else {
- /* Copy path to reinsert leading slash */
- scopy(&buf2[1], len - 1, path);
- path = buf2;
- *path = sep;
- }
- *ppath = path;
- }
- if (pquery) {
- *pquery = query;
- }
- if (preference) {
- *preference = reference;
- }
- if (pext) {
- #if ME_WIN_LIKE
- slower(ext);
- #endif
- *pext = ext;
- }
- return 0;
- }
- /*
- Normalize a URI path to remove "./", "../" and redundant separators.
- Note: this does not make an abs path and does not map separators nor change case.
- This validates the URI and expects it to begin with "/".
- Returns an allocated path, caller must free.
- */
- PUBLIC char *websNormalizeUriPath(char *pathArg)
- {
- char *dupPath, *path, *sp, *dp, *mark, **segments;
- int firstc, j, i, nseg, len;
- if (pathArg == 0 || *pathArg == '\0') {
- return sclone("");
- }
- len = (int) slen(pathArg);
- if ((dupPath = walloc(len + 2)) == 0) {
- return NULL;
- }
- strcpy(dupPath, pathArg);
- if ((segments = walloc(sizeof(char*) * (len + 1))) == 0) {
- wfree(dupPath);
- return NULL;
- }
- nseg = len = 0;
- firstc = *dupPath;
- for (mark = sp = dupPath; *sp; sp++) {
- if (*sp == '/') {
- *sp = '\0';
- while (sp[1] == '/') {
- sp++;
- }
- segments[nseg++] = mark;
- len += (int) (sp - mark);
- mark = sp + 1;
- }
- }
- segments[nseg++] = mark;
- len += (int) (sp - mark);
- for (j = i = 0; i < nseg; i++, j++) {
- sp = segments[i];
- if (sp[0] == '.') {
- if (sp[1] == '\0') {
- if ((i+1) == nseg) {
- /* Trim trailing "." */
- segments[j] = "";
- } else {
- j--;
- }
- } else if (sp[1] == '.' && sp[2] == '\0') {
- j = max(j - 2, -1);
- if ((i+1) == nseg) {
- nseg--;
- }
- } else {
- /* .more-chars */
- segments[j] = segments[i];
- }
- } else {
- segments[j] = segments[i];
- }
- }
- nseg = j;
- assert(nseg >= 0);
- if ((path = walloc(len + nseg + 1)) != 0) {
- for (i = 0, dp = path; i < nseg; ) {
- strcpy(dp, segments[i]);
- len = (int) slen(segments[i]);
- dp += len;
- if (++i < nseg || (nseg == 1 && *segments[0] == '\0' && firstc == '/')) {
- *dp++ = '/';
- }
- }
- *dp = '\0';
- }
- wfree(dupPath);
- wfree(segments);
- return path;
- }
- /*
- Validate a URI path for use in a HTTP request line
- The URI must contain only valid characters and must being with "/" both before and after decoding.
- A decoded, normalized URI path is returned.
- The uri is modified. Returns an allocated path. Caller must free.
- */
- PUBLIC char *websValidateUriPath(char *uri)
- {
- if (uri == 0 || *uri != '/') {
- return 0;
- }
- if (!websValidUriChars(uri)) {
- return 0;
- }
- websDecodeUrl(uri, uri, -1);
- if ((uri = websNormalizeUriPath(uri)) == 0) {
- return 0;
- }
- if (*uri != '/' || strchr(uri, '\\')) {
- wfree(uri);
- return 0;
- }
- return uri;
- }
- /*
- Open a web page. filename is the local filename. path is the URL path name.
- */
- PUBLIC int websPageOpen(Webs *wp, int mode, int perm)
- {
- assert(websValid(wp));
- return (wp->docfd = websOpenFile(wp->filename, mode, perm));
- }
- PUBLIC void websPageClose(Webs *wp)
- {
- assert(websValid(wp));
- if (wp->docfd >= 0) {
- websCloseFile(wp->docfd);
- wp->docfd = -1;
- }
- }
- PUBLIC int websPageStat(Webs *wp, WebsFileInfo *sbuf)
- {
- return websStatFile(wp->filename, sbuf);
- }
- PUBLIC int websPageIsDirectory(Webs *wp)
- {
- WebsFileInfo sbuf;
- if (websStatFile(wp->filename, &sbuf) >= 0) {
- return(sbuf.isDir);
- }
- return 0;
- }
- /*
- Read a web page. Returns the number of _bytes_ read. len is the size of buf, in bytes.
- */
- PUBLIC ssize websPageReadData(Webs *wp, char *buf, ssize nBytes)
- {
- assert(websValid(wp));
- return websReadFile(wp->docfd, buf, nBytes);
- }
- /*
- Move file pointer offset bytes.
- */
- PUBLIC void websPageSeek(Webs *wp, Offset offset, int origin)
- {
- assert(websValid(wp));
- websSeekFile(wp->docfd, offset, origin);
- }
- PUBLIC void websSetCookie(Webs *wp, char *name, char *value, char *path, char *cookieDomain, int lifespan, int flags)
- {
- WebsTime when;
- char *cp, *expiresAtt, *expires, *domainAtt, *domain, *secure, *httponly, *cookie, *old;
- assert(wp);
- assert(name && *name);
- if (path == 0) {
- path = "/";
- }
- if (!cookieDomain) {
- domain = sclone(wp->host);
- if ((cp = strchr(domain, ':')) != 0) {
- /* Strip port */
- *cp = '\0';
- }
- if (*domain && domain[strlen(domain) - 1] == '.') {
- /* Cleanup bonjour addresses with trailing dot */
- domain[strlen(domain) - 1] = '\0';
- }
- } else {
- domain = sclone(cookieDomain);
- }
- domainAtt = "";
- if (smatch(domain, "localhost")) {
- wfree(domain);
- domain = sclone("");
- } else {
- domainAtt = "; domain=";
- if (!strchr(domain, '.')) {
- old = domain;
- domain = sfmt(".%s", domain);
- wfree(old);
- }
- }
- if (lifespan > 0) {
- expiresAtt = "; expires=";
- when = time(0) + lifespan;
- if ((expires = ctime(&when)) != NULL) {
- expires[strlen(expires) - 1] = '\0';
- }
- } else {
- expiresAtt = "";
- expires = "";
- }
- /*
- Allow multiple cookie headers. Even if the same name. Later definitions take precedence
- */
- secure = (flags & WEBS_COOKIE_SECURE) ? "; secure" : "";
- httponly = (flags & WEBS_COOKIE_HTTP) ? "; httponly" : "";
- cookie = sfmt("%s=%s; path=%s%s%s%s%s%s%s", name, value, path, domainAtt, domain, expiresAtt, expires, secure,
- httponly);
- if (wp->responseCookie) {
- old = wp->responseCookie;
- wp->responseCookie = sfmt("%s %s", wp->responseCookie, cookie);
- wfree(old);
- wfree(cookie);
- } else {
- wp->responseCookie = cookie;
- }
- wfree(domain);
- }
- /*
- Return the next token in the input stream. Does not allocate
- */
- static char *getToken(Webs *wp, char *delim)
- {
- WebsBuf *buf;
- char *token, *nextToken, *endToken;
- assert(wp);
- buf = &wp->rxbuf;
- nextToken = (char*) buf->endp;
- for (token = (char*) buf->servp; (*token == ' ' || *token == '\t') && token < (char*) buf->endp; token++) {}
- if (delim == 0) {
- delim = " \t";
- if ((endToken = strpbrk(token, delim)) != 0) {
- nextToken = endToken + strspn(endToken, delim);
- *endToken = '\0';
- }
- } else {
- if ((endToken = strstr(token, delim)) != 0) {
- *endToken = '\0';
- /* Only eat one occurence of the delimiter */
- nextToken = endToken + strlen(delim);
- } else {
- nextToken = buf->endp;
- }
- }
- buf->servp = nextToken;
- return token;
- }
- PUBLIC int websGetBackground()
- {
- return websBackground;
- }
- PUBLIC void websSetBackground(int on)
- {
- websBackground = on;
- }
- PUBLIC int websGetDebug()
- {
- return websDebug;
- }
- PUBLIC void websSetDebug(int on)
- {
- websDebug = on;
- }
- static char *makeSessionID(Webs *wp)
- {
- char idBuf[64];
- static int nextSession = 0;
- assert(wp);
- fmt(idBuf, sizeof(idBuf), "%08x%08x%d", PTOI(wp) + PTOI(wp->url), (int) time(0), nextSession++);
- return websMD5Block(idBuf, slen(idBuf), "::webs.session::");
- }
- PUBLIC void websDestroySession(Webs *wp)
- {
- websGetSession(wp, 0);
- if (wp->session) {
- hashDelete(sessions, wp->session->id);
- sessionCount--;
- freeSession(wp->session);
- wp->session = 0;
- }
- }
- PUBLIC WebsSession *websCreateSession(Webs *wp)
- {
- websDestroySession(wp);
- return websGetSession(wp, 1);
- }
- WebsSession *websAllocSession(Webs *wp, char *id, int lifespan)
- {
- WebsSession *sp;
- assert(wp);
- if ((sp = walloc(sizeof(WebsSession))) == 0) {
- return 0;
- }
- sp->lifespan = lifespan;
- sp->expires = time(0) + lifespan;
- if (id == 0) {
- sp->id = makeSessionID(wp);
- } else {
- sp->id = sclone(id);
- }
- if ((sp->cache = hashCreate(WEBS_SESSION_HASH)) == 0) {
- wfree(sp->id);
- wfree(sp);
- return 0;
- }
- if (hashEnter(sessions, sp->id, valueSymbol(sp), 0) == 0) {
- wfree(sp->id);
- wfree(sp);
- return 0;
- }
- return sp;
- }
- static void freeSession(WebsSession *sp)
- {
- assert(sp);
- if (sp->cache >= 0) {
- hashFree(sp->cache);
- sp->cache = -1;
- }
- wfree(sp->id);
- wfree(sp);
- }
- WebsSession *websGetSession(Webs *wp, int create)
- {
- WebsKey *sym;
- char *id;
- assert(wp);
- if (!wp->session) {
- id = websGetSessionID(wp);
- if ((sym = hashLookup(sessions, id)) == 0) {
- if (!create) {
- wfree(id);
- return 0;
- }
- if (sessionCount > ME_GOAHEAD_LIMIT_SESSION_COUNT) {
- printf("Too many sessions %d/%d", sessionCount, ME_GOAHEAD_LIMIT_SESSION_COUNT);
- wfree(id);
- return 0;
- }
- sessionCount++;
- if ((wp->session = websAllocSession(wp, id, ME_GOAHEAD_LIMIT_SESSION_LIFE)) == 0) {
- wfree(id);
- return 0;
- }
- websSetCookie(wp, WEBS_SESSION, wp->session->id, "/", NULL, 0, 0);
- } else {
- wp->session = (WebsSession*) sym->content.value.symbol;
- }
- wfree(id);
- }
- if (wp->session) {
- wp->session->expires = time(0) + wp->session->lifespan;
- }
- return wp->session;
- }
- static char *websParseCookie(Webs *wp, char *name)
- {
- cchar *cookie;
- char *cp, *value;
- ssize nlen;
- int quoted;
- assert(wp);
- if ((cookie = wp->cookie) == 0 || name == 0 || *name == '\0') {
- return 0;
- }
- nlen = slen(name);
- while ((value = strstr(cookie, name)) != 0) {
- /* Ignore corrupt cookies of the form "name=;" */
- if ((value == cookie || value[-1] == ' ' || value[-1] == ';') && value[nlen] == '=' && value[nlen+1] != ';') {
- break;
- }
- cookie += nlen;
- }
- if (value == 0) {
- return 0;
- }
- value += nlen;
- while (isspace((uchar) *value) || *value == '=') {
- value++;
- }
- quoted = 0;
- if (*value == '"') {
- value++;
- quoted++;
- }
- for (cp = value; *cp; cp++) {
- if (quoted) {
- if (*cp == '"' && cp[-1] != '\\') {
- break;
- }
- } else {
- if ((*cp == ',' || *cp == ';') && cp[-1] != '\\') {
- break;
- }
- }
- }
- return snclone(value, cp - value);
- }
- PUBLIC char *websGetSessionID(Webs *wp)
- {
- assert(wp);
- if (wp->session) {
- return wp->session->id;
- }
- return websParseCookie(wp, WEBS_SESSION);
- }
- PUBLIC char *websGetSessionVar(Webs *wp, char *key, char *defaultValue)
- {
- WebsSession *sp;
- WebsKey *sym;
- assert(wp);
- assert(key && *key);
- if ((sp = websGetSession(wp, 1)) != 0) {
- if ((sym = hashLookup(sp->cache, key)) == 0) {
- return defaultValue;
- }
- return (char*) sym->content.value.symbol;
- }
- return 0;
- }
- PUBLIC void websRemoveSessionVar(Webs *wp, char *key)
- {
- WebsSession *sp;
- assert(wp);
- assert(key && *key);
- if ((sp = websGetSession(wp, 1)) != 0) {
- hashDelete(sp->cache, key);
- }
- }
- PUBLIC int websSetSessionVar(Webs *wp, char *key, char *value)
- {
- WebsSession *sp;
- assert(wp);
- assert(key && *key);
- assert(value);
- if ((sp = websGetSession(wp, 1)) == 0) {
- return 0;
- }
- if (hashEnter(sp->cache, key, valueString(value, VALUE_ALLOCATE), 0) == 0) {
- return -1;
- }
- return 0;
- }
- static void pruneSessions()
- {
- WebsSession *sp;
- WebsTime when;
- WebsKey *sym, *next;
- int oldCount;
- if (sessions >= 0) {
- oldCount = sessionCount;
- when = time(0);
- for (sym = hashFirst(sessions); sym; sym = next) {
- next = hashNext(sessions, sym);
- sp = (WebsSession*) sym->content.value.symbol;
- if (sp->expires <= when) {
- hashDelete(sessions, sp->id);
- sessionCount--;
- freeSession(sp);
- }
- }
- // if (oldCount != sessionCount || sessionCount) {
- // printf("Prune %d sessions. Remaining: %d", oldCount - sessionCount, sessionCount);
- // }
- }
- websRestartEvent(pruneId, WEBS_SESSION_PRUNE);
- }
- static void freeSessions()
- {
- WebsSession *sp;
- WebsKey *sym, *next;
- if (sessions >= 0) {
- for (sym = hashFirst(sessions); sym; sym = next) {
- next = hashNext(sessions, sym);
- sp = (WebsSession*) sym->content.value.symbol;
- hashDelete(sessions, sp->id);
- freeSession(sp);
- }
- hashFree(sessions);
- sessions = -1;
- }
- }
- /*
- One line embedding
- */
- PUBLIC int websServer(char *endpoint, char *documents)
- {
- int finished = 0;
- if (websOpen(documents, "route.txt") < 0) {
- printf("Cannot initialize server. Exiting.");
- return -1;
- }
- if (websLoad("auth.txt") < 0) {
- printf("Cannot load auth.txt");
- return -1;
- }
- if (websListen(endpoint) < 0) {
- return -1;
- }
- websServiceEvents(&finished);
- websClose();
- return 0;
- }
- static void setFileLimits()
- {
- #if ME_UNIX_LIKE
- struct rlimit r;
- int i, limit;
- limit = ME_GOAHEAD_LIMIT_FILES;
- if (limit == 0) {
- /*
- We need to determine a reasonable maximum possible limit value.
- There is no #define we can use for this, so we test to determine it empirically
- */
- for (limit = 0x40000000; limit > 0; limit >>= 1) {
- r.rlim_cur = r.rlim_max = limit;
- if (setrlimit(RLIMIT_NOFILE, &r) == 0) {
- for (i = (limit >> 4) * 15; i > 0; i--) {
- r.rlim_max = r.rlim_cur = limit + i;
- if (setrlimit(RLIMIT_NOFILE, &r) == 0) {
- limit = 0;
- break;
- }
- }
- break;
- }
- }
- } else {
- r.rlim_cur = r.rlim_max = limit;
- if (setrlimit(RLIMIT_NOFILE, &r) < 0) {
- printf("Cannot set file limit to %d", limit);
- }
- }
- getrlimit(RLIMIT_NOFILE, &r);
- //printf("Max files soft %d, max %d", (int)r.rlim_cur, (int)r.rlim_max);
- #endif
- }
- /*
- Output an error message and cleanup
- */
- PUBLIC void websError(Webs *wp, int code, char *fmt, ...)
- {
- va_list args;
- char *msg, *buf;
- char *encoded;
- int status;
- assert(wp);
- wp->error++;
- if (code & WEBS_CLOSE) {
- wp->flags &= ~WEBS_KEEP_ALIVE;
- wp->connError++;
- }
- status = code & WEBS_CODE_MASK;
- #if !ME_ROM
- if (wp->putfd >= 0) {
- close(wp->putfd);
- wp->putfd = -1;
- }
- #endif
- if (wp->rxRemaining && status != 200 && status != 301 && status != 302 && status != 401) {
- /* Close connection so we don't have to consume remaining content */
- wp->flags &= ~WEBS_KEEP_ALIVE;
- }
- encoded = websEscapeHtml(wp->url);
- wfree(wp->url);
- wp->url = encoded;
- if (fmt) {
- if (!(code & WEBS_NOLOG)) {
- va_start(args, fmt);
- msg = sfmtv(fmt, args);
- va_end(args);
- printf( "%s", msg);
- wfree(msg);
- }
- buf = sfmt("\
- <html>\r\n\
- <head><title>Document Error: %s</title></head>\r\n\
- <body>\r\n\
- <h2>Access Error: %s</h2>\r\n\
- </body>\r\n\
- </html>\r\n", websErrorMsg(code), websErrorMsg(code));
- } else {
- buf = 0;
- }
- websResponse(wp, code, buf);
- wfree(buf);
- }
- /*
- Return the error message for a given code
- */
- PUBLIC char *websErrorMsg(int code)
- {
- WebsError *ep;
- assert(code >= 0);
- code &= WEBS_CODE_MASK;
- for (ep = websErrors; ep->code; ep++) {
- if (code == ep->code) {
- return ep->msg;
- }
- }
- return websErrorMsg(HTTP_CODE_INTERNAL_SERVER_ERROR);
- }
- /*
- Accessors
- */
- PUBLIC char *websGetCookie(Webs *wp) { return wp->cookie; }
- PUBLIC char *websGetDir(Webs *wp) { return wp->route && wp->route->dir ? wp->route->dir : websGetDocuments(); }
- PUBLIC int websGetEof(Webs *wp) { return wp->eof; }
- PUBLIC char *websGetExt(Webs *wp) { return wp->ext; }
- PUBLIC char *websGetFilename(Webs *wp) { return wp->filename; }
- PUBLIC char *websGetHost(Webs *wp) { return wp->host; }
- PUBLIC char *websGetIfaddr(Webs *wp) { return wp->ifaddr; }
- PUBLIC char *websGetIpaddr(Webs *wp) { return wp->ipaddr; }
- PUBLIC char *websGetMethod(Webs *wp) { return wp->method; }
- PUBLIC char *websGetPassword(Webs *wp) { return wp->password; }
- PUBLIC char *websGetPath(Webs *wp) { return wp->path; }
- PUBLIC int websGetPort(Webs *wp) { return wp->port; }
- PUBLIC char *websGetProtocol(Webs *wp) { return wp->protocol; }
- PUBLIC char *websGetQuery(Webs *wp) { return wp->query; }
- PUBLIC char *websGetServer() { return websHost; }
- PUBLIC char *websGetServerAddress() { return websIpAddr; }
- PUBLIC char *websGetServerAddressUrl() { return websIpAddrUrl; }
- PUBLIC char *websGetServerUrl() { return websHostUrl; }
- PUBLIC char *websGetUrl(Webs *wp) { return wp->url; }
- PUBLIC char *websGetUserAgent(Webs *wp) { return wp->userAgent; }
- PUBLIC char *websGetUsername(Webs *wp) { return wp->username; }
- /*
- Copyright (c) Embedthis Software. All Rights Reserved.
- This software is distributed under commercial and open source licenses.
- You may use the Embedthis GoAhead open source license or you may acquire
- a commercial license from Embedthis Software. You agree to be fully bound
- by the terms of either license. Consult the LICENSE.md distributed with
- this software for full details and other copyrights.
- */
|