1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409 |
- /*
- 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();
- logOpen();
- 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) {
- error("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();
- logClose();
- 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) {
- error("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) {
- error("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) {
- error("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) {
- error("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) {
- error("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) {
- error("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) {
- error("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)
- {
- error("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)
- {
- error("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)) {
- error("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) {
- error("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) {
- error("Cannot initialize server. Exiting.");
- return -1;
- }
- if (websLoad("auth.txt") < 0) {
- error("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) {
- error("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.
- */
|