Ë èbÙKé™Fãó*—dZdgZddlZddlZddlZej d«Zej d«ZGd„de«Z d„Z d „Z d „Z Gd „d «Z Gd „d«ZGd„d«ZGd„d«ZGd„d«Zd„Zd„Zd„Zd„Zd„Zd„Zd„Zd„Zy)a& Middleware to check for obedience to the WSGI specification. Some of the things this checks: * Signature of the application and start_response (including that keyword arguments are not used). * Environment checks: - Environment is a dictionary (and not a subclass). - That all the required keys are in the environment: REQUEST_METHOD, SERVER_NAME, SERVER_PORT, wsgi.version, wsgi.input, wsgi.errors, wsgi.multithread, wsgi.multiprocess, wsgi.run_once - That HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH are not in the environment (these headers should appear as CONTENT_LENGTH and CONTENT_TYPE). - Warns if QUERY_STRING is missing, as the cgi module acts unpredictably in that case. - That CGI-style variables (that don't contain a .) have (non-unicode) string values - That wsgi.version is a tuple - That wsgi.url_scheme is 'http' or 'https' (@@: is this too restrictive?) - Warns if the REQUEST_METHOD is not known (@@: probably too restrictive). - That SCRIPT_NAME and PATH_INFO are empty or start with / - That at least one of SCRIPT_NAME or PATH_INFO are set. - That CONTENT_LENGTH is a positive integer. - That SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should be '/'). - That wsgi.input has the methods read, readline, readlines, and __iter__ - That wsgi.errors has the methods flush, write, writelines * The status is a string, contains a space, starts with an integer, and that integer is in range (> 100). * That the headers is a list (not a subclass, not another kind of sequence). * That the items of the headers are tuples of strings. * That there is no 'status' header (that is used in CGI, but not in WSGI). * That the headers don't contain newlines or colons, end in _ or -, or contain characters codes below 037. * That Content-Type is given if there is content (CGI often has a default content type, but WSGI does not). * That no Content-Type is given when there is no content (@@: is this too restrictive?) * That the exc_info argument to start_response is a tuple or None. * That all calls to the writer are with strings, and no other methods on the writer are accessed. * That wsgi.input is used properly: - .read() is called with exactly one argument - That it returns a string - That readline, readlines, and __iter__ return strings - That .close() is not called - No other methods are provided * That wsgi.errors is used properly: - .write() and .writelines() is called with a string - That .close() is not called, and no other methods are provided. * The response iterator: - That it is not a string (it should be a list of a single string; a string will work, but perform horribly). - That .__next__() returns a string - That the iterator is not iterated over until start_response has been called (that can signal either a server or application error). - That .close() is called (doesn't raise exception, only prints to sys.stderr, because we only know it isn't called when the object is garbage collected). Ú validatoréNz^[a-zA-Z][a-zA-Z0-9\-_]*$z [\000-\037]có—eZdZdZy)Ú WSGIWarningz: Raised in response to WSGI-spec-related warnings N)Ú__name__Ú __module__Ú __qualname__Ú__doc__©óú'/usr/lib/python3.12/wsgiref/validate.pyrrys„òr rcó—|st|Ž‚y©N)ÚAssertionError)ÚcondÚargss r Úassert_r~s€Ù ܘdÐ#Ð#ð r cóp—t|«tur|Stdj|t |«««‚)Nz!{0} must be of type str (got {1}))ÚtypeÚstrrÚformatÚrepr)ÚvalueÚtitles r Úcheck_string_typer‚s6€Ü ˆUƒ|”sÑØˆ Ü Ø+×2Ñ2°5¼$¸u»+ÓFó HðHr có‡—ˆfd„}|S)a® When applied between a WSGI server and a WSGI application, this middleware will check for WSGI compliance on a number of levels. This middleware does not modify the request or response in any way, but will raise an AssertionError if anything seems off (except for a failure to close the application iterator, which will be printed to stderr -- there's no way to raise an exception at that point). có0•‡‡—tt|«dk(d«t| d«|\}Št|«gŠˆˆfd„}t|d«|d<t |d«|d<‰||«}t|duxr|dk7d«t |«t |‰«S) NézTwo arguments requiredúNo keyword arguments allowedcóL•—tt|«dk(xst|«dk(d|›�«t| d«|d}|d}t|«dk(r|d}nd}t|«t|«t ||«t |«‰j d«t‰|Ž«S)NrézInvalid number of arguments: rré)rÚlenÚ check_statusÚ check_headersÚcheck_content_typeÚcheck_exc_infoÚappendÚ WriteWrapper)rÚkwÚstatusÚheadersÚexc_infoÚstart_responseÚstart_response_starteds €€r Ústart_response_wrapperz;validator..lint_app..start_response_wrapperŸs¡ø€Ü ”C˜“I ‘NÒ4¤c¨$£i°1¡nøÙ59Ð;ô >ä ˜�FÐ:Ô ;ؘ!‘WˆFؘ1‘gˆGÜ�4‹y˜AŠ~Ø ™7‘à�ä ˜Ô Ü ˜'Ô "Ü ˜v wÔ /Ü ˜8Ô $à "× )Ñ )¨$Ô /Ü¡°Ð 5Ó6Ð 6r ú wsgi.inputú wsgi.errorsFz>The application must return an iterator, if only an empty list)rr"Ú check_environÚ InputWrapperÚ ErrorWrapperÚcheck_iteratorÚIteratorWrapper)rr)Úenvironr/Úiteratorr-r.Ú applications @@€r Úlint_appzvalidator..lint_app”s®ú€Ü”�D“ ˜Q‘Ð 8Ô9Ü�B�Ð6Ô7Ø"&ш�ä�gÔð"$Ðõ 7ô&!-¨W°\Ñ-BÓ Cˆ� ÑÜ!-¨g°mÑ.DÓ!Eˆ� Ñá˜wÐ(>Ó?ˆÜ� Ð$Ò:¨°UÑ):Ø Lô Nô �xÔ ä˜xÐ)?Ó@Ð@r r )r9r:s` r rrˆsø€ô'AðR €Or có0—eZdZd„Zd„Zd„Zd„Zd„Zd„Zy)r3có—||_yr)Úinput)ÚselfÚ wsgi_inputs r Ú__init__zInputWrapper.__init__Ás €Øˆ� r cóš—tt|«dk(«|jj|Ž}tt |«t u«|S©Nr!)rr"r=ÚreadrÚbytes©r>rÚvs r rCzInputWrapper.readÄs<€Ü”�D“ ˜Q‘ÔØ ˆD�J‰J�O‰O˜TÐ "ˆÜ”�Q“œ5Ð Ô!؈r cóš—tt|«dk«|jj|Ž}tt |«t u«|SrB)rr"r=ÚreadlinerrDrEs r rHzInputWrapper.readlineÊs>€Ü”�D“ ˜Q‘ÔØ ˆD�J‰J× Ñ  Ð &ˆÜ”�Q“œ5Ð Ô!؈r cóÜ—tt|«dk«|jj|Ž}tt |«t u«|D]}tt |«t u«Œ|SrB)rr"r=Ú readlinesrÚlistrD)r>rÚlinesÚlines r rJzInputWrapper.readlinesÐsY€Ü”�D“ ˜Q‘ÔØ$�— ‘ ×$Ñ$ dÐ+ˆÜ”�U“ œtÐ#Ô$ÛˆDÜ ”D˜“J¤%Ð'Õ (ðàˆ r c#ó`K—|j«x}r|–—|j«x}rŒyy­wr)rH)r>rMs r Ú__iter__zInputWrapper.__iter__Øs/èø€Ø—m‘m“oÐ%ˆdÐ%ØŠJð—m‘m“oÐ%ˆdÓ%ùs‚).¬.có—tdd«y)Nrz input.close() must not be called©r©r>s r ÚclosezInputWrapper.closeÜs €Ü�Ð5Õ6r N) rrrr@rCrHrJrOrSr r r r3r3¿s „ò òò ò òó7r r3có*—eZdZd„Zd„Zd„Zd„Zd„Zy)r4có—||_yr)Úerrors)r>Ú wsgi_errorss r r@zErrorWrapper.__init__áó €Ø!ˆ� r cón—tt|«tu«|jj |«yr)rrrrVÚwrite©r>Úss r rZzErrorWrapper.writeäs$€Ü”�Q“œ3�ÔØ � ‰ ×ј!Õr có8—|jj«yr)rVÚflushrRs r r^zErrorWrapper.flushès€Ø � ‰ ×ÑÕr có4—|D]}|j|«Œyr)rZ)r>ÚseqrMs r Ú writelineszErrorWrapper.writelinesës€ÛˆDØ �J‰J�tÕ ñr có—tdd«y)Nrz!errors.close() must not be calledrQrRs r rSzErrorWrapper.closeïs €Ü�Ð6Õ7r N)rrrr@rZr^rarSr r r r4r4ßs„ò"òòòó8r r4có—eZdZd„Zd„Zy)r(có—||_yr)Úwriter)r>Ú wsgi_writers r r@zWriteWrapper.__init__ôrXr cóZ—tt|«tu«|j|«yr)rrrDrer[s r Ú__call__zWriteWrapper.__call__÷s€Ü”�Q“œ5Ð Ô!Ø � ‰ �A�r N)rrrr@rhr r r r(r(òs „ò"ór r(có—eZdZd„Zd„Zy)ÚPartialIteratorWrappercó—||_yr©r8)r>Ú wsgi_iterators r r@zPartialIteratorWrapper.__init__ýs €Ø%ˆ� r có.—t|jd«Sr)r6r8rRs r rOzPartialIteratorWrapper.__iter__s€ä˜tŸ}™}¨dÓ3Ð3r N)rrrr@rOr r r rjrjûs „ò&ó4r rjcó*—eZdZd„Zd„Zd„Zd„Zd„Zy)r6cóN—||_t|«|_d|_||_y)NF)Úoriginal_iteratorÚiterr8ÚclosedÚcheck_start_response)r>rmrts r r@zIteratorWrapper.__init__s%€Ø!.ˆÔܘ]Ó+ˆŒ ؈Œ Ø$8ˆÕ!r có—|Srr rRs r rOzIteratorWrapper.__iter__ s€Øˆ r cóò—t|j d«t|j«}t |«t urtdd|›d�«|j �t|j d«d|_|S)NzIterator read after closedFz!Iterator yielded non-bytestring (Ú)zjThe application returns and we started iterating over its body, but start_response has not yet been called)rrsÚnextr8rrDrt)r>rFs r Ú__next__zIteratorWrapper.__next__si€Ü�D—K‘K�Ø (ô *ä �—‘Ó ˆÜ �‹7œ%Ñ Ü ‘EÂQÐHÔ IØ × $Ñ $Ð 0Ü �D×-Ñ-Ø|ô ~à(,ˆDÔ %؈r cót—d|_t|jd«r|jj«yy)NTrS)rsÚhasattrrqrSrRs r rSzIteratorWrapper.closes0€ØˆŒ Ü �4×)Ñ)¨7Ô 3Ø × "Ñ "× (Ñ (Õ *ð 4r có†—|jstjjd«t |jd«y)Nz/Iterator garbage collected without being closed)rsÚsysÚstderrrZrrRs r Ú__del__zIteratorWrapper.__del__ s0€Ø�{Š{Ü �J‰J× Ñ ØAô Cä�— ‘ Ø =õ ?r N)rrrr@rOryrSrr r r r6r6s„ò9ò ò ò+ó ?r r6c ób—tt|«tudt|«›d|›d�«dD]}t||vd|›�«ŒdD]}t||vd|›d|d d›d �«Œd |vrtjd t «|j «D]>}d |vrŒtt||«tud|›dt||«›d||›d�«Œ@tt|d«tud|d›d�«t|ddvd|dz«t|d«t|d«|ddvr tjd|dzt «t|jd« xs|djd«d|dz«t|jd« xs|djd«d|dz«|jd «r!tt|d «d!k\d"|d z«|jd«std|vd#«t|jd«dk7d$«y)%Nz&Environment is not of the right type: z (environment: rw) ÚREQUEST_METHODÚ SERVER_NAMEÚ SERVER_PORTú wsgi.versionr0r1zwsgi.multithreadzwsgi.multiprocessz wsgi.run_oncez"Environment missing required key: )ÚHTTP_CONTENT_TYPEÚHTTP_CONTENT_LENGTHz%Environment should not have the key: z (use éz instead)Ú QUERY_STRINGz’QUERY_STRING is not in the WSGI environment; the cgi module will use sys.argv when this variable is missing, so application errors are more likelyÚ.zEnvironmental variable z is not a string: z (value: r„z wsgi.version should be a tuple (zwsgi.url_scheme)ÚhttpÚhttpszwsgi.url_scheme unknown: %rr0r1r�)ÚGETÚHEADÚPOSTÚOPTIONSÚPATCHÚPUTÚDELETEÚTRACEzUnknown REQUEST_METHOD: %rÚ SCRIPT_NAMEÚ/z$SCRIPT_NAME doesn't start with /: %rÚ PATH_INFOz"PATH_INFO doesn't start with /: %rÚCONTENT_LENGTHrzInvalid CONTENT_LENGTH: %rzgOne of SCRIPT_NAME or PATH_INFO are required (PATH_INFO should at least be '/' if SCRIPT_NAME is empty)zOSCRIPT_NAME cannot be '/'; it should instead be '', and PATH_INFO should be '/')rrÚdictÚwarningsÚwarnrÚkeysrÚtupleÚ check_inputÚ check_errorsÚgetÚ startswithÚint)r7Úkeys r r2r2'sn€Ü ŒD�‹MœTÑ !ä ��=š'ð #ô$ó!ˆô ��w‘Ù69Ð ;õ =ð !ó<ˆÜ�˜7Ñ"â"% s¨1¨2¢wð 0õ 1ð<ð ˜WÑ$Ü� ‰ ð 4ô ô  ð �|‰|Ž~ˆØ �#‰:à Ü”�W˜S‘\Ó"¤cÑ)â”D˜ ™Õ&¨°« ð 6õ 7ð ô ŒD�˜Ñ(Ó )¬UÑ 2Ø18¸Ó1HÐJôLä ˆGÐ%Ñ &Ð*;Ð ;Ø%¨Ð0AÑ(BÑBôDô�˜ Ñ%Ô&Ü�˜Ñ'Ô(ðÐÑ ð)MñMä� ‰ Ø (¨7Ð3CÑ+DÑ DÜ ô ô �— ‘ ˜MÓ*Ð *ò 6Ø�}Ñ%×0Ñ0°Ó5Ø.°¸Ñ1GÑGôIô �— ‘ ˜KÓ(Ð (ò 4Ø�{Ñ#×.Ñ.¨sÓ3Ø,¨w°{Ñ/CÑCôEð‡{�{Ð#Ô$Ü”�GÐ,Ñ-Ó.°!Ñ3Ø (¨7Ð3CÑ+DÑ Dô Fð �;‰;�}Ô %Ü� ˜wÐ&ð >ô ?ô ˆG�K‰K˜ Ó &¨#Ñ -ð "õ#r cóJ—dD]}tt||«d|›d|›�«Œ y)N)rCrHrJrOz wsgi.input (ú) doesn't have the attribute ©rr{)r?Úattrs r r�r�hs'€Û=ˆÜ”˜  DÔ)â™4ð !õ "ñ>r cóJ—dD]}tt||«d|›d|›�«Œ y)N)r^rZraz wsgi.errors (r¤r¥)rWr¦s r ržržns'€Û0ˆÜ”˜  TÔ*â™Dð "õ #ñ1r có—t|d«}|jdd«d}tt|«dk(d|z«t |«}t|dk\d|z«t|«dks|dd k7rt j d |zt«yy) NÚStatusr!rr z)Status codes must be three characters: %rédzStatus code is invalid: %réÚ zjThe status string (%r) should be a three-digit integer followed by a single space and a status explanation)rÚsplitrr"r¡r™ršr)r*Ú status_codeÚ status_ints r r#r#ts—€Ü ˜v xÓ 0€Fà—,‘,˜t QÓ'¨Ñ*€KÜ ŒC� Ó  Ñ !Ø3°kÑAôCä�[Ó!€JÜ ˆJ˜#Ñ Ð;¸jÑHÔIÜ ˆ6ƒ{�Q‚˜& ™) sÒ*Ü� ‰ ð Bàñ ä!õ #ð+r c óÖ—tt|«tud|›dt|«›�«|D�]9}tt|«tud|›dt|«›�«tt |«dk(«|\}}t |d«}t |d«}t|j «dk7d |z«td |vxrd |vd |z«ttj|«d |z«t|jd« xr|jd« d|z«tj|«s�Œtdd|›dtj|«jd«›d�«�Œ<y)Nz Headers (z) must be of type list: zIndividual headers (z) must be of type tuple: rú Header namez Header valuer*zyThe Status header cannot be used; it conflicts with CGI script, and HTTP status is not given through headers (value: %r).Ú Ú:z,Header names may not contain ':' or '\n': %rzBad header name: %rÚ-Ú_z#Names may not end in '-' or '_': %rrzBad header value: z (bad char: rw) rrrKrœr"rÚlowerÚ header_reÚsearchÚendswithÚbad_header_value_reÚgroup)r+ÚitemÚnamers r r$r$‚sM€Ü ŒD�‹MœTÑ !â ”D˜”Mð #ô$ôˆÜ”�T“ œeÑ#â”T˜$”Zð !ô "ô ”�D“ ˜Q‘ÔØ‰ ˆˆeÜ   }Ó5ˆÜ! %¨Ó8ˆÜ�— ‘ “  Ñ(ð à"ñ #ô $ô �˜DÐ Ò4 S° _Ø ;¸dÑ Bô Dä” × Ñ  Ó&Ð(=ÀÑ(DÔEÜ�D—M‘M #Ó&Ð&ÒA¨t¯}©}¸SÓ/AÐ+AØ 1°DÑ 8ô :ä × %Ñ % eÖ ,Ü ‘AÚÔ)×0Ñ0°Ó7×=Ñ=¸aÕ@ðBö Cñ%r có—t|d«}t|jdd«d«}d}|D]:\}}t|d«}|j«dk(sŒ&||vryt dd|z«Œ<||vrt dd|z«yy) Nr©r!r)éÌi0r±z content-typezJContent-Type header found in a %s response, which must not return content.z,No Content-Type header found in headers (%s))rr¡r­r¶r)r*r+ÚcodeÚNO_MESSAGE_BODYr½rs r r%r%›sš€Ü ˜v xÓ 0€FÜ ˆv�|‰|˜D !Ó$ QÑ'Ó (€Dð!€OÛ‰ ˆˆeÜ   }Ó5ˆØ �:‰:‹<˜>Ó )ؘ?Ñ*ÙÜ �Að9Ø<@ñAõ Bð ð �?Ñ"Ü�ÐAÀGÑKÕLð#r c ód—t|duxst|«tud|›dt|«›�«y)Nz exc_info (z) is not a tuple: )rrrœ)r,s r r&r&«s-€Ü ˆH˜Ð Ò 7¤ X£´%Ð 7øÚ.6¼¸X¼ÐGõIr cóF—tt|ttf« d«y)NzwYou should not return a string as your application iterator, instead return a single-item list containing a bytestring.)rÚ isinstancerrDrls r r5r5°s#€ô ” ˜8¤c¬5 \Ó2Ð 2ð EõFr )r Ú__all__Úrer}r™Úcompiler·rºÚWarningrrrrr3r4r(rjr6r2r�ržr#r$r%r&r5r r r ÚrÉsÀðñ iðT ˆ-€ó Û Ûà ˆB�J‰JÐ3Ó 4€ Ø �b—j‘j Ó0Ðô�'ôò $òHò 5÷n7ñ7÷@8ñ8÷&ñ÷4ñ4÷!?ñ!?òF?#òB"ò #ò #òCò2Mò Ió Fr