[
  {
    "path": "CustomHighlightSyntax.msyn",
    "content": "[CustomSyntax]\nName=Custom\nUseRegex=1\nUnderline=[^A-Za-z_&-](http(s)?://[A-Za-z0-9_.&?=%~#{}()@+-]+:?[A-Za-z0-9_./&?=%~#{}()@+-]+)[^A-Za-z0-9_-]\nRed=[^A-Za-z0-9_\\-](loss|down|deny|disabled?|unknown|fault|shutdown|disconnected|error|failed|denied|not permitted|disallowed|not allowed|refused|Attack occurred|problem|failure|not permitted|notconnect|service-type|forbidden-ip|excluded-ip-address|sysname|hostname|arp static|ip ((rpf-)?route(-static)?(-group)?|forward-broadcast)|user-bind static|static-bind|stp( global)?|lacp|reboot|(mac-address )?blackhole)[^A-Za-z0-9_\\-]\nGreen=[^A-Za-z0-9_\\-](received|recovered|permit( vlan)?|(allow-pass|pvid|access|default|hybrid) vlan|allowed|enabled?|successful(ly)?|available|connected|up|yes|ok)[^A-Za-z0-9_\\-]\nYellow=[^A-Za-z0-9_\\-]([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)[^A-Za-z0-9_\\-]\nBlue=[^A-Za-z0-9]((interface )?(((X|Ten-|M-)?Gigabit|Fiber)?(Ethernet) ?|(100|40|25|X|F)?(G|T)?(E|i|ei)|(Twenty-Five|Forty|Hundred)?GigE ?|BAGG|NULL|LoopBack|Bridge-Aggregation|AggregatePort|Eth-Trunk|Vlan-interface|Vlanif)-?[0-9]+(\\/[0-9]+)*|acl( (name [A-Za-z0-9&_-]+ [0-9]+|number ?[0-9]+))?|ospf( [0-9]+)?|(ip-pool|ip pool) [A-Za-z0-9 :\"'\\.\\\\\\/&_\\-]+)[^A-Za-z0-9]\nMagenta=[^A-Za-z0-9_\\-]([0-9a-f][0-9a-f][0-9a-f][0-9a-f](:|-)[0-9a-f][0-9a-f][0-9a-f][0-9a-f](:|-)[0-9a-f][0-9a-f][0-9a-f][0-9a-f]|[0-9a-f][0-9a-f](:|-)[0-9a-f][0-9a-f](:|-)[0-9a-f][0-9a-f](:|-)[0-9a-f][0-9a-f](:|-)[0-9a-f][0-9a-f](:|-)[0-9a-f][0-9a-f]|(Please )?Press ENTER( to get started)?\\.?)[^A-Za-z0-9_\\-]\nCyan=[^A-Za-z0-9_\\-]((no|undo|description)( [A-Za-z0-9 :\"'\\.\\\\\\/&_\\-]+)?|acl [0-9]+|dhcp( (server( (enable|detect|database|option ?[0-9]+))?|select( interface)?|relay|enable|snooping))?|rule [0-9]+|source(-port)?|destination(-port)?|snmp-agent|import-route|traffic-policy [A-Za-z0-9_\\-]+ (in|out)bound|protocol (in|out)bound [A-za-z]+|authentication-mode( (aaa|scheme|password))?|(un)?tagged)[^A-Za-z0-9_\\-]\nBlinking=\nCaseSensitive=0\n\n"
  },
  {
    "path": "DoNotProxy.list",
    "content": "DOMAIN-SUFFIX,local\nDOMAIN-SUFFIX,localhost\nDOMAIN-SUFFIX,internal\nDOMAIN-SUFFIX,lan\nDOMAIN-SUFFIX,ip6-localhost\nDOMAIN-SUFFIX,ip6-loopback\nIP-CIDR,0.0.0.0/8,no-resolve\nIP-CIDR,10.0.0.0/8,no-resolve\nIP-CIDR,100.64.0.0/10,no-resolve\nIP-CIDR,127.0.0.0/8,no-resolve\nIP-CIDR,169.254.0.0/16,no-resolve\nIP-CIDR,172.16.0.0/12,no-resolve\nIP-CIDR,192.168.0.0/16,no-resolve\nIP-CIDR,198.18.0.0/16,no-resolve\nIP-CIDR,224.0.0.0/4,no-resolve\nIP-CIDR6,::1/128,no-resolve\nIP-CIDR6,fc00::/7,no-resolve\nIP-CIDR6,fe80::/10,no-resolve\nIP-CIDR6,fd00::/8,no-resolve\nDOMAIN-KEYWORD,aria2\nDOMAIN-KEYWORD,announce\nDOMAIN-KEYWORD,tracker\nDOMAIN,injections.adguard.org\nDOMAIN,local.adguard.org\nPROCESS-NAME,aria2c.exe\nPROCESS-NAME,fdm.exe\nPROCESS-NAME,Folx.exe\nPROCESS-NAME,NetTransport.exe\nPROCESS-NAME,Thunder.exe\nPROCESS-NAME,Transmission.exe\nPROCESS-NAME,uTorrent.exe\nPROCESS-NAME,Azureus.exe\nPROCESS-NAME,deluge.exe\nPROCESS-NAME,BitComet.exe\nPROCESS-NAME,BitComet_x64.exe\nPROCESS-NAME,qbittorrent.exe\nPROCESS-NAME,WebTorrent.exe\nPROCESS-NAME,WebTorrent Helper.exe\nPROCESS-NAME,rufus.exe\nPROCESS-NAME,DownloadService.exe\nPROCESS-NAME,Weiyun.exe\nPROCESS-NAME,BaiduNetdisk.exe\nPROCESS-NAME,aDrive.exe\nPROCESS-NAME,Quark.exe\n"
  },
  {
    "path": "DoProxy.list",
    "content": "# Basic on https://raw.githubusercontent.com/kn007/patch/master/bypass.list\n\nDOMAIN-SUFFIX,c.gle\nDOMAIN-SUFFIX,wallhaven.cc\nDOMAIN-SUFFIX,manhuagui.com\nDOMAIN-SUFFIX,catbox.moe\nDOMAIN-SUFFIX,mixtape.moe\nDOMAIN-SUFFIX,maxroll.gg\nDOMAIN-SUFFIX,simaware.ca\nDOMAIN-SUFFIX,arcgisonline.com\nDOMAIN-SUFFIX,rainviewer.com\nDOMAIN-SUFFIX,csb.app\nDOMAIN-SUFFIX,codesandbox.io\nDOMAIN-SUFFIX,subscriptionsmanagement-pa.googleapis.com\nDOMAIN-SUFFIX,subscriptionsmobile-pa.googleapis.com\nDOMAIN-SUFFIX,phosphor-pa.googleapis.com\nDOMAIN-SUFFIX,growth-pa.googleapis.com\nDOMAIN-SUFFIX,cloud.cupronickel.goog\nDOMAIN-SUFFIX,openai.com\nDOMAIN-SUFFIX,twitch.tv\nDOMAIN-SUFFIX,bose.com\nDOMAIN-SUFFIX,beautifyconverter.com\nDOMAIN-SUFFIX,worldofwarships.asia\nDOMAIN-SUFFIX,worldofwarships.jp\nDOMAIN-SUFFIX,wargaming.net\nDOMAIN-SUFFIX,hikarifield.co.jp\nDOMAIN-SUFFIX,postfix.org\nDOMAIN-SUFFIX,ietf.org\nDOMAIN-SUFFIX,schema.org\nDOMAIN-SUFFIX,youtu.be\nDOMAIN-SUFFIX,doubleclick.net\nDOMAIN-SUFFIX,googleadservices.com\nDOMAIN-SUFFIX,google.cn\nDOMAIN-SUFFIX,googleapps.com\nDOMAIN-SUFFIX,thinkwithgoogle.com\nDOMAIN-SUFFIX,withgoogle.com\nDOMAIN-SUFFIX,fbcdn.net\nDOMAIN-SUFFIX,flickr.com\nDOMAIN-SUFFIX,youtube-nocookie.com\nDOMAIN-SUFFIX,youtube.com\nDOMAIN-SUFFIX,googleusercontent.com\nDOMAIN-SUFFIX,gstatic.com\nDOMAIN-SUFFIX,thepiratebay.se\nDOMAIN-SUFFIX,gmail.com\nDOMAIN-SUFFIX,facebook.com\nDOMAIN-SUFFIX,vimeo.com\nDOMAIN-SUFFIX,twitter.com\nDOMAIN-SUFFIX,staticflickr.com\nDOMAIN-SUFFIX,sstatic.net\nDOMAIN-SUFFIX,imgur.com\nDOMAIN-SUFFIX,fastly.net\nDOMAIN-SUFFIX,bit.ly\nDOMAIN-SUFFIX,newrelic.com\nDOMAIN-SUFFIX,paypal.com\nDOMAIN-SUFFIX,paypalobjects.com\nDOMAIN-SUFFIX,cloudfront.net\nDOMAIN-SUFFIX,googlepages.com\nDOMAIN-SUFFIX,twimg.com\nDOMAIN-SUFFIX,t.co\nDOMAIN-SUFFIX,ytimg.com\nDOMAIN-SUFFIX,adzerk.net\nDOMAIN-SUFFIX,xmages.net\nDOMAIN-SUFFIX,fucklo.li\nDOMAIN-SUFFIX,freeproxylists.net\nDOMAIN-SUFFIX,archive.org\nDOMAIN-SUFFIX,shadowsocks.org\nDOMAIN-SUFFIX,dropbox.com\nDOMAIN-SUFFIX,blogspot.com\nDOMAIN-SUFFIX,rage4.com\nDOMAIN-SUFFIX,googleapis.com\nDOMAIN-SUFFIX,brandonchecketts.com\nDOMAIN-SUFFIX,googleratings.com\nDOMAIN-SUFFIX,letscorp.net\nDOMAIN-SUFFIX,googlevideo.com\nDOMAIN-SUFFIX,gravatar.com\nDOMAIN-SUFFIX,goods-pro.com\nDOMAIN-SUFFIX,w.org\nDOMAIN-SUFFIX,wp.com\nDOMAIN-SUFFIX,deviantart.com\nDOMAIN-SUFFIX,deviantart.net\nDOMAIN-SUFFIX,archive.fo\nDOMAIN-SUFFIX,blogger.com\nDOMAIN-SUFFIX,wordpress.com\nDOMAIN-SUFFIX,instagram.com\nDOMAIN-SUFFIX,facebook.net\nDOMAIN-SUFFIX,github.io\nDOMAIN-SUFFIX,github.com\nDOMAIN-SUFFIX,githubusercontent.com\nDOMAIN-SUFFIX,ssl-images-amazon.com\nDOMAIN-SUFFIX,associates-amazon.com\nDOMAIN-SUFFIX,media-amazon.com\nDOMAIN-SUFFIX,awsstatic.com\nDOMAIN-SUFFIX,googlecode.com\nDOMAIN-SUFFIX,live.com\nDOMAIN-SUFFIX,onedriver.com\nDOMAIN-SUFFIX,wikipedia.org\nDOMAIN-SUFFIX,*.wikimedia.org\nDOMAIN-SUFFIX,nodequery.com\nDOMAIN-SUFFIX,androidfilehost.com\nDOMAIN-SUFFIX,appspot.com\nDOMAIN-SUFFIX,pastebin.com\nDOMAIN-SUFFIX,dropboxstatic.com\nDOMAIN-SUFFIX,disq.us\nDOMAIN-SUFFIX,disquscdn.com\nDOMAIN-SUFFIX,disqus.com\nDOMAIN-SUFFIX,alexa.com\nDOMAIN-SUFFIX,amazonaws.com\nDOMAIN-SUFFIX,bbc.co.uk\nDOMAIN-SUFFIX,travis-ci.org\nDOMAIN-SUFFIX,pki.goog\nDOMAIN-SUFFIX,cdninstagram.com\nDOMAIN-SUFFIX,wordpress.org\nDOMAIN-SUFFIX,slideshare.net\nDOMAIN-SUFFIX,textnow.com\nDOMAIN-SUFFIX,gfx.ms\nDOMAIN-SUFFIX,chromium.org\nDOMAIN-SUFFIX,goo.gl\nDOMAIN-SUFFIX,googleblog.com\nDOMAIN-SUFFIX,wikileaks.org\nDOMAIN-SUFFIX,ggpht.com\nDOMAIN-SUFFIX,t.me\nDOMAIN-SUFFIX,telegram.me\nDOMAIN-SUFFIX,telegra.ph\nDOMAIN-SUFFIX,telegram.org\nDOMAIN-SUFFIX,tdesktop.com\nDOMAIN-SUFFIX,cisco.com\nDOMAIN-SUFFIX,oneplus.net\nDOMAIN-SUFFIX,g.co\nDOMAIN-SUFFIX,amazon.com\nDOMAIN-SUFFIX,xda-developers.com\nDOMAIN-SUFFIX,mysql.com\nDOMAIN-SUFFIX,tiny.cc\nDOMAIN-SUFFIX,speedyrails.net\nDOMAIN-SUFFIX,androidfilehost.com\nDOMAIN-SUFFIX,pinimg.com\nDOMAIN-SUFFIX,flightradar24.com\nDOMAIN-SUFFIX,dropboxusercontent.com\nDOMAIN-SUFFIX,cloudconvert.com\nDOMAIN-SUFFIX,steaminventoryhelper.com\nDOMAIN-SUFFIX,steamcardexchange.net\nDOMAIN-SUFFIX,steamcommunity.com\nDOMAIN-SUFFIX,steampowered.com\nDOMAIN-SUFFIX,steam-api.com\nDOMAIN-SUFFIX,akamaihd.net\nDOMAIN-SUFFIX,keycdn.com\nDOMAIN-SUFFIX,pumpcloud.net\nDOMAIN-SUFFIX,mega.nz\nDOMAIN-SUFFIX,mega.co.nz\nDOMAIN-SUFFIX,telesco.pe\nDOMAIN-SUFFIX,discordapp.net\nDOMAIN-SUFFIX,discordapp.com\nDOMAIN-SUFFIX,discord.com\nDOMAIN-SUFFIX,discord.gg\nDOMAIN-SUFFIX,redd.it\nDOMAIN-SUFFIX,reddit.com\nDOMAIN-SUFFIX,redditstatic.com\nDOMAIN-SUFFIX,redditmedia.com\nDOMAIN-SUFFIX,rawgit.com\nDOMAIN-SUFFIX,ampproject.net\nDOMAIN-SUFFIX,windy.com\nDOMAIN-SUFFIX,githubassets.com\nDOMAIN-SUFFIX,bootstrapcdn.com\nDOMAIN-SUFFIX,ubi.com\nDOMAIN-SUFFIX,ubisoft.com\nDOMAIN-SUFFIX,wonderpush.com\nDOMAIN-SUFFIX,goo.gl\nDOMAIN-SUFFIX,similarweb.com\nDOMAIN-SUFFIX,similarcdn.com\nDOMAIN-SUFFIX,intercom.io\nDOMAIN-SUFFIX,intercomcdn.com\nDOMAIN-SUFFIX,regex101.com\nDOMAIN-SUFFIX,jsfiddle.net\nDOMAIN-SUFFIX,jshell.net\nDOMAIN-SUFFIX,tunemymusic.com\nDOMAIN-SUFFIX,unsplash.com\nDOMAIN-SUFFIX,ping.pe\nDOMAIN-SUFFIX,spotify.com\nDOMAIN-SUFFIX,scdn.co\nDOMAIN-SUFFIX,3dmark.com\nDOMAIN-SUFFIX,futuremark.com\nDOMAIN-SUFFIX,ultravps.eu\nDOMAIN-SUFFIX,providerdienste.de\nDOMAIN-SUFFIX,virtualhosts.de\nDOMAIN-SUFFIX,he.net\nDOMAIN-SUFFIX,akamaized.net\nDOMAIN-SUFFIX,typekit.net\nDOMAIN-SUFFIX,akamaihd.net\nDOMAIN-SUFFIX,jquery.com\nDOMAIN-SUFFIX,ubi.li\nDOMAIN-SUFFIX,github.blog\nDOMAIN-SUFFIX,steamdb.info\nDOMAIN-SUFFIX,algolia.net\nDOMAIN-SUFFIX,steamstatic.com\nDOMAIN-SUFFIX,pixiv.net\nDOMAIN-SUFFIX,pximg.net\nDOMAIN-SUFFIX,pixnet.net\nDOMAIN-SUFFIX,pixfs.net\nDOMAIN-SUFFIX,sharemods.com\nDOMAIN-SUFFIX,notion.so\nDOMAIN-SUFFIX,imgbox.com\nDOMAIN-SUFFIX,geeks3d.com\nDOMAIN-SUFFIX,nvidia.com\nDOMAIN-SUFFIX,gamepadviewer.com\nDOMAIN-SUFFIX,humansofnewyork.com\nDOMAIN-SUFFIX,cloudflare.com\nDOMAIN-SUFFIX,wagnardsoft.com\nDOMAIN-SUFFIX,gitlab.com\nDOMAIN-SUFFIX,gitlab-static.net\nDOMAIN-SUFFIX,soundcloud.com\nDOMAIN-SUFFIX,sndcdn.com\nDOMAIN-SUFFIX,dl.sourceforge.net\nDOMAIN-SUFFIX,javmost.com\nDOMAIN-SUFFIX,javtrust.com\nDOMAIN-SUFFIX,jable.tv\nDOMAIN-SUFFIX,dmm.co.jp\nDOMAIN-SUFFIX,tomyangsh.pw\nDOMAIN-SUFFIX,rouman5.com\nDOMAIN-SUFFIX,rou.video\nDOMAIN-SUFFIX,htpt.cc\nDOMAIN-SUFFIX,soulvoice.club\nDOMAIN-SUFFIX,m-team.cc\nDOMAIN-SUFFIX,m-team.io\nDOMAIN-SUFFIX,hdcmct.org\nDOMAIN-SUFFIX,springsunday.net\nDOMAIN-SUFFIX,pterclub.com\nDOMAIN-SUFFIX,totheglory.im\nDOMAIN-SUFFIX,imgurl.org\nDOMAIN-SUFFIX,flycrow.pro\nDOMAIN-SUFFIX,groueta.cc\nDOMAIN-SUFFIX,mantou.biz\nDOMAIN-SUFFIX,ccache.org\nDOMAIN-SUFFIX,seejav.bid\nDOMAIN-SUFFIX,picturedata.org\nDOMAIN-SUFFIX,javbee.net\nDOMAIN-SUFFIX,bmp.ovh\nDOMAIN-SUFFIX,open.cd\nDOMAIN-SUFFIX,dmhy.org\nDOMAIN-SUFFIX,keepfrds.com\nDOMAIN-SUFFIX,jdbimgs.com\nDOMAIN-SUFFIX,gifyu.com\nDOMAIN-SUFFIX,dmmsee.fun\nDOMAIN-SUFFIX,bitvise.com\nDOMAIN-SUFFIX,chucklefish.org\nDOMAIN-SUFFIX,steamgames.com\nDOMAIN-SUFFIX,steamcontent.com\nDOMAIN-SUFFIX,steamusercontent.com\nDOMAIN-SUFFIX,fast.com\nDOMAIN-SUFFIX,netflix.com\nDOMAIN-SUFFIX,nflxvideo.net\nDOMAIN-SUFFIX,nflxext.com\nDOMAIN-SUFFIX,nflxso.net\nDOMAIN-SUFFIX,onetrust.com\nDOMAIN-SUFFIX,fosshub.com\nDOMAIN-SUFFIX,autodesk.com.cn\nDOMAIN-SUFFIX,autodesk.com\nDOMAIN-SUFFIX,autodesk.net\nDOMAIN-SUFFIX,openvpn.net\nDOMAIN-SUFFIX,webflow.io\nDOMAIN-SUFFIX,webflow.com\nDOMAIN-SUFFIX,pstorage.space\nDOMAIN-SUFFIX,netcdn.space\nDOMAIN-SUFFIX,boost.org\nDOMAIN-SUFFIX,tarolink.top\nDOMAIN-SUFFIX,o--o.xyz\nDOMAIN-SUFFIX,simgbb.com\nDOMAIN-SUFFIX,imgbb.com\nDOMAIN-SUFFIX,ibb.co\nDOMAIN-SUFFIX,z4a.net\nDOMAIN-SUFFIX,iili.io\nDOMAIN-SUFFIX,weserv.nl\nDOMAIN-SUFFIX,shoot.photo\nDOMAIN-SUFFIX,ccp.ovh\nDOMAIN-SUFFIX,imagebam.com\nDOMAIN-SUFFIX,truenas.com\nDOMAIN-SUFFIX,themoneyconverter.com\nDOMAIN-SUFFIX,jpopsuki.eu\nDOMAIN-SUFFIX,skyvector.com\nDOMAIN-SUFFIX,postimgs.org\nDOMAIN-SUFFIX,postlmg.cc\nDOMAIN-SUFFIX,imagecurl.com\nDOMAIN-SUFFIX,simbrief.com\nDOMAIN-SUFFIX,flightsim.to\nDOMAIN-SUFFIX,flybywiresim.com\nDOMAIN-SUFFIX,chartfox.org\nDOMAIN-SUFFIX,openstreetmap.org\nDOMAIN-SUFFIX,vatsim.net\nDOMAIN-SUFFIX,navigraph.com\nDOMAIN-SUFFIX,gog-statics.com\nDOMAIN-SUFFIX,gog.com\nDOMAIN-SUFFIX,zendesk.com\nDOMAIN-SUFFIX,zdassets.com\nDOMAIN-SUFFIX,avsim.com\nDOMAIN-SUFFIX,sda1.dev\nDOMAIN-SUFFIX,sl.al\nDOMAIN-SUFFIX,ccimg.xyz\nDOMAIN-SUFFIX,admod.com\nDOMAIN-SUFFIX,web.dev\nDOMAIN-SUFFIX,jsdelivr.net\nDOMAIN-SUFFIX,jsdelivr.com\nDOMAIN-SUFFIX,evga.com\nDOMAIN-SUFFIX,adultempire.com\nDOMAIN-SUFFIX,imageshack.com\nDOMAIN-SUFFIX,exoticaz.to\nDOMAIN-SUFFIX,storages.cc\nDOMAIN-SUFFIX,rockstargames.com\nDOMAIN-SUFFIX,arkoselabs.com\nDOMAIN-SUFFIX,arkoselabs.cn\nDOMAIN-SUFFIX,javhdporn.net\nDOMAIN-SUFFIX,i18n.pw\nDOMAIN-SUFFIX,qbittorrent.org\nDOMAIN-SUFFIX,seejav.bid\nDOMAIN-SUFFIX,jdbstatic.com\nDOMAIN-SUFFIX,021jf.com\nDOMAIN-SUFFIX,pic599.net\nDOMAIN-SUFFIX,98tuch.net\nDOMAIN-SUFFIX,postto.me\nDOMAIN-SUFFIX,aspnetcdn.com\nDOMAIN-SUFFIX,netlify.app\nDOMAIN-SUFFIX,netlify.com\nDOMAIN-SUFFIX,healthchecks.io\nDOMAIN-SUFFIX,mgstage.com\nDOMAIN-SUFFIX,gvt2.com\nDOMAIN-SUFFIX,gstatic.com\nDOMAIN-SUFFIX,googleapis.com\nDOMAIN-SUFFIX,gmodules.com\nDOMAIN-SUFFIX,googlesyndication.com\nDOMAIN-SUFFIX,sesexiaozhan.com\nDOMAIN-SUFFIX,biquge.tw\nDOMAIN-SUFFIX,acgnx.se\nDOMAIN-SUFFIX,combot.org\nDOMAIN-SUFFIX,tenor.com\nDOMAIN-SUFFIX,pixeldrain.com\nDOMAIN-SUFFIX,gamer.com.tw\nDOMAIN-SUFFIX,bahamut.com.tw\nDOMAIN-SUFFIX,seiya-saiga.com\nDOMAIN-SUFFIX,ptpimg.me\nDOMAIN-SUFFIX,wiki.gg\nDOMAIN-SUFFIX,1024search.tk\nDOMAIN-SUFFIX,1080.tw\nDOMAIN-SUFFIX,1688.com.au\nDOMAIN-SUFFIX,1dpw.com\nDOMAIN-SUFFIX,2008xianzhang.info\nDOMAIN-SUFFIX,24smile.org\nDOMAIN-SUFFIX,4shared.com\nDOMAIN-SUFFIX,5i01.com\nDOMAIN-SUFFIX,5z5.com\nDOMAIN-SUFFIX,64memo.com\nDOMAIN-SUFFIX,64tianwang.com\nDOMAIN-SUFFIX,64wiki.com\nDOMAIN-SUFFIX,666kb.com\nDOMAIN-SUFFIX,6do.news\nDOMAIN-SUFFIX,6park.com\nDOMAIN-SUFFIX,a1080hd.com\nDOMAIN-SUFFIX,abc.xyz\nDOMAIN-SUFFIX,ablwang.com\nDOMAIN-SUFFIX,aboluowang.com\nDOMAIN-SUFFIX,actimes.com.au\nDOMAIN-SUFFIX,adsafeprotected.com\nDOMAIN-SUFFIX,adsrvr.org\nDOMAIN-SUFFIX,adultblogranking.com\nDOMAIN-SUFFIX,aforcemorepowerful.org\nDOMAIN-SUFFIX,ahd1080.com\nDOMAIN-SUFFIX,aisex.com\nDOMAIN-SUFFIX,aishangyou.tube\nDOMAIN-SUFFIX,ait.org.tw\nDOMAIN-SUFFIX,alabout.com\nDOMAIN-SUFFIX,alicejapan.co.jp\nDOMAIN-SUFFIX,aliengu.com\nDOMAIN-SUFFIX,alliance.org.hk\nDOMAIN-SUFFIX,allinfa.com\nDOMAIN-SUFFIX,am730.com.hk\nDOMAIN-SUFFIX,amazon.co.jp\nDOMAIN-SUFFIX,amazonwebapps.com\nDOMAIN-SUFFIX,amnesty.org\nDOMAIN-SUFFIX,amnesty.tw\nDOMAIN-SUFFIX,ananass.fr\nDOMAIN-SUFFIX,android.com\nDOMAIN-SUFFIX,androidfilehost.com\nDOMAIN-SUFFIX,animecrazy.net\nDOMAIN-SUFFIX,anti1984.com\nDOMAIN-SUFFIX,anygoing.com\nDOMAIN-SUFFIX,aomiwang.com\nDOMAIN-SUFFIX,aoqinet.com\nDOMAIN-SUFFIX,apkmirror.com\nDOMAIN-SUFFIX,app2.hkatv.com\nDOMAIN-SUFFIX,appledaily.hk\nDOMAIN-SUFFIX,appspot.com\nDOMAIN-SUFFIX,archive.org\nDOMAIN-SUFFIX,asahichinese.com\nDOMAIN-SUFFIX,asianews.it\nDOMAIN-SUFFIX,atchinese.com\nDOMAIN-SUFFIX,atdmt.com\nDOMAIN-SUFFIX,atgfw.org\nDOMAIN-SUFFIX,aurl.mobi\nDOMAIN-SUFFIX,ausdaily.net.au\nDOMAIN-SUFFIX,autoit-cdn.com\nDOMAIN-SUFFIX,autoitscript.com\nDOMAIN-SUFFIX,avbbs.tv\nDOMAIN-SUFFIX,avcity.tv\nDOMAIN-SUFFIX,avlang.com\nDOMAIN-SUFFIX,av-scouter.info\nDOMAIN-SUFFIX,avsp2p.com\nDOMAIN-SUFFIX,backchina.com\nDOMAIN-SUFFIX,backtotiananmen.com\nDOMAIN-SUFFIX,badongo.com\nDOMAIN-SUFFIX,baisex.me\nDOMAIN-SUFFIX,bannedbook.org\nDOMAIN-SUFFIX,bayfiles.net\nDOMAIN-SUFFIX,bbcchinese.com\nDOMAIN-SUFFIX,bbg.gov\nDOMAIN-SUFFIX,bcchinese.net\nDOMAIN-SUFFIX,beijingspring.com\nDOMAIN-SUFFIX,bet365.com\nDOMAIN-SUFFIX,betanews.com\nDOMAIN-SUFFIX,beyondfirewall.com\nDOMAIN-SUFFIX,bind9.net\nDOMAIN-SUFFIX,binux.me\nDOMAIN-SUFFIX,bit.ly\nDOMAIN-SUFFIX,bitshare.com\nDOMAIN-SUFFIX,bitsnoop.com\nDOMAIN-SUFFIX,biz.tm\nDOMAIN-SUFFIX,bjs.org\nDOMAIN-SUFFIX,bjzc.org\nDOMAIN-SUFFIX,blinkx.com\nDOMAIN-SUFFIX,blockedinchina.net\nDOMAIN-SUFFIX,blog.jp\nDOMAIN-SUFFIX,blog.xuite.net\nDOMAIN-SUFFIX,blog.yam.com\nDOMAIN-SUFFIX,blogblog.com\nDOMAIN-SUFFIX,blogcatalog.com\nDOMAIN-SUFFIX,blogcity.me\nDOMAIN-SUFFIX,blogimg.jp\nDOMAIN-SUFFIX,bloglines.com\nDOMAIN-SUFFIX,bloglovin.com\nDOMAIN-SUFFIX,blogs.com\nDOMAIN-SUFFIX,blogspot.in\nDOMAIN-SUFFIX,blogspot.kr\nDOMAIN-SUFFIX,bmvflorida.us\nDOMAIN-SUFFIX,bod.asia\nDOMAIN-SUFFIX,book.com.tw\nDOMAIN-SUFFIX,books.com.tw\nDOMAIN-SUFFIX,botanwang.com\nDOMAIN-SUFFIX,botanwang.org\nDOMAIN-SUFFIX,bowenpress.com\nDOMAIN-SUFFIX,boxun.com\nDOMAIN-SUFFIX,boxun.us\nDOMAIN-SUFFIX,break.com\nDOMAIN-SUFFIX,brizzly.com\nDOMAIN-SUFFIX,btdigg.org\nDOMAIN-SUFFIX,btku.org\nDOMAIN-SUFFIX,btn.weather.ca\nDOMAIN-SUFFIX,bud.org.tw\nDOMAIN-SUFFIX,buff.ly\nDOMAIN-SUFFIX,bullog.org\nDOMAIN-SUFFIX,bullogger.com\nDOMAIN-SUFFIX,businessweek.com\nDOMAIN-SUFFIX,bwp.im\nDOMAIN-SUFFIX,bx.tl\nDOMAIN-SUFFIX,c000.me\nDOMAIN-SUFFIX,c080.me\nDOMAIN-SUFFIX,c800.me\nDOMAIN-SUFFIX,c9x.info\nDOMAIN-SUFFIX,cahr.org.tw\nDOMAIN-SUFFIX,campaign.tw-npo.org\nDOMAIN-SUFFIX,cams.org.sg\nDOMAIN-SUFFIX,canyu.org\nDOMAIN-SUFFIX,caochangqing.com\nDOMAIN-SUFFIX,cap.org.hk\nDOMAIN-SUFFIX,cari.com.my\nDOMAIN-SUFFIX,caribbeancom.com\nDOMAIN-SUFFIX,catholic.org.hk\nDOMAIN-SUFFIX,catholic.org.tw\nDOMAIN-SUFFIX,catwizard.net\nDOMAIN-SUFFIX,cbc.ca\nDOMAIN-SUFFIX,ccdtr.org\nDOMAIN-SUFFIX,ccim.org\nDOMAIN-SUFFIX,ccw.org.tw\nDOMAIN-SUFFIX,cdef.org\nDOMAIN-SUFFIX,cdjp.org\nDOMAIN-SUFFIX,cdnews.com.tw\nDOMAIN-SUFFIX,cdns.com.tw\nDOMAIN-SUFFIX,cecc.gov\nDOMAIN-SUFFIX,cenews.eu\nDOMAIN-SUFFIX,centralnation.com\nDOMAIN-SUFFIX,cfhks.org.hk\nDOMAIN-SUFFIX,cgdepot.org\nDOMAIN-SUFFIX,chicagoncmtv.com\nDOMAIN-SUFFIX,china.ucanews.com\nDOMAIN-SUFFIX,chinaaid.net\nDOMAIN-SUFFIX,chinabiz.org.tw\nDOMAIN-SUFFIX,chinadigitaltimes.net\nDOMAIN-SUFFIX,chinaelections.com\nDOMAIN-SUFFIX,chinaelections.org\nDOMAIN-SUFFIX,chinaeweekly.com\nDOMAIN-SUFFIX,chinafile.com\nDOMAIN-SUFFIX,chinagfw.org\nDOMAIN-SUFFIX,chinainperspective.com\nDOMAIN-SUFFIX,chinapress.com.my\nDOMAIN-SUFFIX,chinarightsia.org\nDOMAIN-SUFFIX,chinatcc.gov.cn\nDOMAIN-SUFFIX,china-week.com\nDOMAIN-SUFFIX,chinaworker.info\nDOMAIN-SUFFIX,chinesedaily.com\nDOMAIN-SUFFIX,chinesenewsnet.com\nDOMAIN-SUFFIX,chinesepen.org\nDOMAIN-SUFFIX,chosun.com\nDOMAIN-SUFFIX,christianstudy.com\nDOMAIN-SUFFIX,christiantimes.org.hk\nDOMAIN-SUFFIX,chrlawyers.hk\nDOMAIN-SUFFIX,chrome.com\nDOMAIN-SUFFIX,chromium.org\nDOMAIN-SUFFIX,chubun.com\nDOMAIN-SUFFIX,cincainews.com\nDOMAIN-SUFFIX,citizenlab.org\nDOMAIN-SUFFIX,citizensradio.org\nDOMAIN-SUFFIX,city9x.com\nDOMAIN-SUFFIX,civicparty.hk\nDOMAIN-SUFFIX,civilhrfront.org\nDOMAIN-SUFFIX,civilmedia.tw\nDOMAIN-SUFFIX,clickme.net\nDOMAIN-SUFFIX,cna.com.tw\nDOMAIN-SUFFIX,cnd.org\nDOMAIN-SUFFIX,cnliberals.com\nDOMAIN-SUFFIX,cnn.com\nDOMAIN-SUFFIX,cnyes.com\nDOMAIN-SUFFIX,codeproject.com\nDOMAIN-SUFFIX,comefromchina.com\nDOMAIN-SUFFIX,competitionforce.hk\nDOMAIN-SUFFIX,cool18.com\nDOMAIN-SUFFIX,coolloud.org.tw\nDOMAIN-SUFFIX,cotweet.com\nDOMAIN-SUFFIX,crazys.cc\nDOMAIN-SUFFIX,creaders.net\nDOMAIN-SUFFIX,creadersnet.com\nDOMAIN-SUFFIX,crwdcntrl.net\nDOMAIN-SUFFIX,c--spanarchives.-org\nDOMAIN-SUFFIX,c-spanvideo.org\nDOMAIN-SUFFIX,cts.com.tw\nDOMAIN-SUFFIX,cw.com.tw\nDOMAIN-SUFFIX,d100.net\nDOMAIN-SUFFIX,d2pass.com\nDOMAIN-SUFFIX,dadazim.com\nDOMAIN-SUFFIX,dailymotion.com\nDOMAIN-SUFFIX,dalailamaworld.com\nDOMAIN-SUFFIX,danwei.org\nDOMAIN-SUFFIX,daolan.net\nDOMAIN-SUFFIX,del.icio.us\nDOMAIN-SUFFIX,democraticchina.org\nDOMAIN-SUFFIX,democrats.org\nDOMAIN-SUFFIX,de-sci.org\nDOMAIN-SUFFIX,dfs.kuaipan.cn\nDOMAIN-SUFFIX,dfzdaili.com\nDOMAIN-SUFFIX,dieneueepoche.com\nDOMAIN-SUFFIX,digg.com\nDOMAIN-SUFFIX,digitalocean.com\nDOMAIN-SUFFIX,digitalvolcano.co.uk\nDOMAIN-SUFFIX,diigo.com\nDOMAIN-SUFFIX,dipity.com\nDOMAIN-SUFFIX,discuss.com.hk\nDOMAIN-SUFFIX,disp.cc\nDOMAIN-SUFFIX,disqus.com\nDOMAIN-SUFFIX,djjsq.com\nDOMAIN-SUFFIX,djorz.com\nDOMAIN-SUFFIX,dnscrypt.org\nDOMAIN-SUFFIX,doit.im\nDOMAIN-SUFFIX,dolc.de\nDOMAIN-SUFFIX,dolf.org.hk\nDOMAIN-SUFFIX,dongtaiwang.com\nDOMAIN-SUFFIX,doub.io\nDOMAIN-SUFFIX,doubibackup.com\nDOMAIN-SUFFIX,download.aircrack-ng.org\nDOMAIN-SUFFIX,dphk.org\nDOMAIN-SUFFIX,dpp.org.tw\nDOMAIN-SUFFIX,dropbox.com\nDOMAIN-SUFFIX,dropboxusercontent.com\nDOMAIN-SUFFIX,drsunacademy.com\nDOMAIN-SUFFIX,dtiblog.com\nDOMAIN-SUFFIX,duga.jp\nDOMAIN-SUFFIX,duihua.org\nDOMAIN-SUFFIX,duping.net\nDOMAIN-SUFFIX,dupola.com\nDOMAIN-SUFFIX,dw.com\nDOMAIN-SUFFIX,dw.de\nDOMAIN-SUFFIX,dw-world.com\nDOMAIN-SUFFIX,dw-world.de\nDOMAIN-SUFFIX,dxlive.com\nDOMAIN-SUFFIX,e123.hk\nDOMAIN-SUFFIX,ebookbrowse.com\nDOMAIN-SUFFIX,ecfa.org.tw\nDOMAIN-SUFFIX,echinanews.com.tw\nDOMAIN-SUFFIX,edge.liveleak.com\nDOMAIN-SUFFIX,edicypages.com\nDOMAIN-SUFFIX,edoors.com\nDOMAIN-SUFFIX,efcc.org.hk\nDOMAIN-SUFFIX,eic-av.com\nDOMAIN-SUFFIX,e-info.org.tw\nDOMAIN-SUFFIX,elpais.com\nDOMAIN-SUFFIX,emilylau.org.hk\nDOMAIN-SUFFIX,erabaru.net\nDOMAIN-SUFFIX,erights.net\nDOMAIN-SUFFIX,eroantenna.com\nDOMAIN-SUFFIX,ero-video.net\nDOMAIN-SUFFIX,es-visiontimes.com\nDOMAIN-SUFFIX,etaiwannews.com\nDOMAIN-SUFFIX,ettoday.net\nDOMAIN-SUFFIX,extremetube.com\nDOMAIN-SUFFIX,extremetube.phncdn.com\nDOMAIN-SUFFIX,facebook.com\nDOMAIN-SUFFIX,facebook.net\nDOMAIN-SUFFIX,fangeming.com\nDOMAIN-SUFFIX,fanqiang.network\nDOMAIN-SUFFIX,fanqianghou.com\nDOMAIN-SUFFIX,farxian.com\nDOMAIN-SUFFIX,fastly.net\nDOMAIN-SUFFIX,faststone.org\nDOMAIN-SUFFIX,favstar.fm\nDOMAIN-SUFFIX,faydao.com\nDOMAIN-SUFFIX,fb.me\nDOMAIN-SUFFIX,fbcdn.net\nDOMAIN-SUFFIX,fc2.com\nDOMAIN-SUFFIX,fdc89.jp\nDOMAIN-SUFFIX,feedburner.com\nDOMAIN-SUFFIX,feedjit.com\nDOMAIN-SUFFIX,feedsportal.com\nDOMAIN-SUFFIX,felixcat.net\nDOMAIN-SUFFIX,ffx.io\nDOMAIN-SUFFIX,filecroco.com\nDOMAIN-SUFFIX,filesor.com\nDOMAIN-SUFFIX,filestube.com\nDOMAIN-SUFFIX,firebaseio.com\nDOMAIN-SUFFIX,firepic.org\nDOMAIN-SUFFIX,flyzy2005.com\nDOMAIN-SUFFIX,fmnnow.com\nDOMAIN-SUFFIX,foofind.is\nDOMAIN-SUFFIX,fooooo.com\nDOMAIN-SUFFIX,forum.kaiyuan.de\nDOMAIN-SUFFIX,forum.tvb.com\nDOMAIN-SUFFIX,fqrouter.com\nDOMAIN-SUFFIX,free4u.com.ar\nDOMAIN-SUFFIX,freebrowser.org\nDOMAIN-SUFFIX,freedomhouse.org\nDOMAIN-SUFFIX,freeproxyserver.net\nDOMAIN-SUFFIX,freeshadow.info\nDOMAIN-SUFFIX,freetufu.com\nDOMAIN-SUFFIX,freewechat.com\nDOMAIN-SUFFIX,freeweibo.com\nDOMAIN-SUFFIX,friendfeed.com\nDOMAIN-SUFFIX,fring.com\nDOMAIN-SUFFIX,frontlinedefenders.org\nDOMAIN-SUFFIX,fukugan.com\nDOMAIN-SUFFIX,fuli.ba\nDOMAIN-SUFFIX,fullyillustrated.com\nDOMAIN-SUFFIX,fungchiwood.com\nDOMAIN-SUFFIX,fw.cm\nDOMAIN-SUFFIX,fw.com\nDOMAIN-SUFFIX,g.co\nDOMAIN-SUFFIX,gaeproxy.com\nDOMAIN-SUFFIX,gamebase.com.tw\nDOMAIN-SUFFIX,gameclub.tw\nDOMAIN-SUFFIX,ganges.com\nDOMAIN-SUFFIX,gcpnews.com\nDOMAIN-SUFFIX,geocities.co.jp\nDOMAIN-SUFFIX,getfoxyproxy.org\nDOMAIN-SUFFIX,getgom.com\nDOMAIN-SUFFIX,getsync.com\nDOMAIN-SUFFIX,ggpht.com\nDOMAIN-SUFFIX,ghd1080.com\nDOMAIN-SUFFIX,gigacircle.com\nDOMAIN-SUFFIX,github.com\nDOMAIN-SUFFIX,git-scm.com\nDOMAIN-SUFFIX,gittigidiyor.com\nDOMAIN-SUFFIX,globalvoices.org\nDOMAIN-SUFFIX,globalvoicesonline.org\nDOMAIN-SUFFIX,glorystar.me\nDOMAIN-SUFFIX,gmail.com\nDOMAIN-SUFFIX,gnews.org\nDOMAIN-SUFFIX,goagent.biz\nDOMAIN-SUFFIX,goo.gl\nDOMAIN-SUFFIX,google.com\nDOMAIN-SUFFIX,googlecode.com\nDOMAIN-SUFFIX,googlesource.com\nDOMAIN-SUFFIX,googletagmanager.com\nDOMAIN-SUFFIX,googleusercontent.com\nDOMAIN-SUFFIX,gopetition.com\nDOMAIN-SUFFIX,gospelherald.com\nDOMAIN-SUFFIX,gospelherald.com.hk\nDOMAIN-SUFFIX,gov.tw\nDOMAIN-SUFFIX,gpass1.com\nDOMAIN-SUFFIX,greatfire.org\nDOMAIN-SUFFIX,greatfirewallofchina.org\nDOMAIN-SUFFIX,greatzhonghua.org\nDOMAIN-SUFFIX,greenparty.org.tw\nDOMAIN-SUFFIX,gvm.com.tw\nDOMAIN-SUFFIX,h528.com\nDOMAIN-SUFFIX,haixiainfo.com.tw\nDOMAIN-SUFFIX,hakkatv.org.tw\nDOMAIN-SUFFIX,hav.tv\nDOMAIN-SUFFIX,have8.com\nDOMAIN-SUFFIX,h-china.org\nDOMAIN-SUFFIX,hcocoa.com\nDOMAIN-SUFFIX,hd1080.org\nDOMAIN-SUFFIX,hdtransform.com\nDOMAIN-SUFFIX,hechaji.com\nDOMAIN-SUFFIX,helpzhuling.org\nDOMAIN-SUFFIX,heqinglian.net\nDOMAIN-SUFFIX,hetnieuwetijdperk.com\nDOMAIN-SUFFIX,hexieshe.com\nDOMAIN-SUFFIX,hikinggfw.org\nDOMAIN-SUFFIX,hilive.tv\nDOMAIN-SUFFIX,hioz.org\nDOMAIN-SUFFIX,hitwister.com\nDOMAIN-SUFFIX,hjav.org\nDOMAIN-SUFFIX,hjclub.info\nDOMAIN-SUFFIX,hk01.com\nDOMAIN-SUFFIX,hkatvnews.com\nDOMAIN-SUFFIX,hkchurch.org\nDOMAIN-SUFFIX,hkci.org.hk\nDOMAIN-SUFFIX,hkcnews.com\nDOMAIN-SUFFIX,hkcrm.org.hk\nDOMAIN-SUFFIX,hkdailynews.com.hk\nDOMAIN-SUFFIX,hkdash.com\nDOMAIN-SUFFIX,hkej.com\nDOMAIN-SUFFIX,hket.com\nDOMAIN-SUFFIX,hketgroup.com\nDOMAIN-SUFFIX,hkgolden.com\nDOMAIN-SUFFIX,hkgoldenmobile.com\nDOMAIN-SUFFIX,hkhkhk.com\nDOMAIN-SUFFIX,hkhrm.org.hk\nDOMAIN-SUFFIX,hkja.org.hk\nDOMAIN-SUFFIX,hkjc.com\nDOMAIN-SUFFIX,hkjp.org\nDOMAIN-SUFFIX,hkptu.org\nDOMAIN-SUFFIX,hk-pub.com\nDOMAIN-SUFFIX,hkreporter.com\nDOMAIN-SUFFIX,hksilicon.com\nDOMAIN-SUFFIX,hkumall.com\nDOMAIN-SUFFIX,hkupop.hku.hk\nDOMAIN-SUFFIX,hkusu.org\nDOMAIN-SUFFIX,hkwcc.org.hk\nDOMAIN-SUFFIX,hongkongfp.com\nDOMAIN-SUFFIX,hongkongtibetfilmfestival2015.com\nDOMAIN-SUFFIX,hotchyx.com\nDOMAIN-SUFFIX,hotspotshield.com\nDOMAIN-SUFFIX,hrichina.org\nDOMAIN-SUFFIX,hrw.org\nDOMAIN-SUFFIX,huanghuagang.org\nDOMAIN-SUFFIX,huaren.us\nDOMAIN-SUFFIX,huaxia-news.com\nDOMAIN-SUFFIX,huping.net\nDOMAIN-SUFFIX,hutong9.net\nDOMAIN-SUFFIX,hwinfo.com\nDOMAIN-SUFFIX,hxmmdd.com\nDOMAIN-SUFFIX,hyperrate.com\nDOMAIN-SUFFIX,hzy.pw\nDOMAIN-SUFFIX,i1.hk\nDOMAIN-SUFFIX,i2ocr.com\nDOMAIN-SUFFIX,i2p2.de\nDOMAIN-SUFFIX,iask.ca\nDOMAIN-SUFFIX,iceimg.com\nDOMAIN-SUFFIX,icij.org\nDOMAIN-SUFFIX,idol-mile.com\nDOMAIN-SUFFIX,idv.tw\nDOMAIN-SUFFIX,ifanqiang.com\nDOMAIN-SUFFIX,ift.tt\nDOMAIN-SUFFIX,igfw.???\nDOMAIN-SUFFIX,igfw.tk\nDOMAIN-SUFFIX,igossip.com\nDOMAIN-SUFFIX,ihao.org\nDOMAIN-SUFFIX,ihd1080.org\nDOMAIN-SUFFIX,ihktv.com\nDOMAIN-SUFFIX,imagebam.com\nDOMAIN-SUFFIX,imageshack.us\nDOMAIN-SUFFIX,imagestorming.com\nDOMAIN-SUFFIX,imageurlhost.com\nDOMAIN-SUFFIX,imagevenue.com\nDOMAIN-SUFFIX,imagezilla.net\nDOMAIN-SUFFIX,img.ly\nDOMAIN-SUFFIX,imgchili.com\nDOMAIN-SUFFIX,imgchili.net\nDOMAIN-SUFFIX,imgdino.com\nDOMAIN-SUFFIX,imgkeep.com\nDOMAIN-SUFFIX,imgly.net\nDOMAIN-SUFFIX,imgtiger.com\nDOMAIN-SUFFIX,immoral.jp\nDOMAIN-SUFFIX,inmediahk.net\nDOMAIN-SUFFIX,inote.tw\nDOMAIN-SUFFIX,inside.com.tw\nDOMAIN-SUFFIX,instagram.com\nDOMAIN-SUFFIX,insynchq.com\nDOMAIN-SUFFIX,intermargins.net\nDOMAIN-SUFFIX,internet.org\nDOMAIN-SUFFIX,internetfreedom.org\nDOMAIN-SUFFIX,inxian.com\nDOMAIN-SUFFIX,ipcf.org.tw\nDOMAIN-SUFFIX,ipicture.ru\nDOMAIN-SUFFIX,ipkmedia.com\nDOMAIN-SUFFIX,isohunt.com\nDOMAIN-SUFFIX,isunaffairs.com\nDOMAIN-SUFFIX,isuntv.com\nDOMAIN-SUFFIX,ithelp.ithome.com.tw\nDOMAIN-SUFFIX,ixxx.com\nDOMAIN-SUFFIX,iyouport.com\nDOMAIN-SUFFIX,iyouport.org\nDOMAIN-SUFFIX,j.mp\nDOMAIN-SUFFIX,jasonsavard.com\nDOMAIN-SUFFIX,jav008.com\nDOMAIN-SUFFIX,javblog.biz\nDOMAIN-SUFFIX,javfree.me\nDOMAIN-SUFFIX,javideo.info\nDOMAIN-SUFFIX,javsharing.com\nDOMAIN-SUFFIX,jbtalks.cc\nDOMAIN-SUFFIX,jinbushe.org\nDOMAIN-SUFFIX,jingfeng.info\nDOMAIN-SUFFIX,jingpin.org\nDOMAIN-SUFFIX,jiruan.net\nDOMAIN-SUFFIX,jjgirls.com\nDOMAIN-SUFFIX,jkforum.net\nDOMAIN-SUFFIX,jpavgod.com\nDOMAIN-SUFFIX,justin.tv\nDOMAIN-SUFFIX,just-ping.com\nDOMAIN-SUFFIX,kan.center\nDOMAIN-SUFFIX,kankan.today\nDOMAIN-SUFFIX,keakon.net\nDOMAIN-SUFFIX,keephkshining.com\nDOMAIN-SUFFIX,kenengba.com\nDOMAIN-SUFFIX,kexueshangwang.info\nDOMAIN-SUFFIX,kinghost.com\nDOMAIN-SUFFIX,kingstone.com.tw\nDOMAIN-SUFFIX,kir.jp\nDOMAIN-SUFFIX,kwongwah.com.my\nDOMAIN-SUFFIX,la-forum.org\nDOMAIN-SUFFIX,lagranepoca.com\nDOMAIN-SUFFIX,lailaibt.com\nDOMAIN-SUFFIX,laqingdan.net\nDOMAIN-SUFFIX,latteye.com\nDOMAIN-SUFFIX,lcx.cc\nDOMAIN-SUFFIX,lefora.com\nDOMAIN-SUFFIX,left21.hk\nDOMAIN-SUFFIX,lemonde.fr\nDOMAIN-SUFFIX,lesoir.be\nDOMAIN-SUFFIX,letscorp.net\nDOMAIN-SUFFIX,libertytimes.com.tw\nDOMAIN-SUFFIX,limbopro.xyz\nDOMAIN-SUFFIX,lineageosrom.com\nDOMAIN-SUFFIX,line-scdn.net\nDOMAIN-SUFFIX,linkbucks.com\nDOMAIN-SUFFIX,listhub.net\nDOMAIN-SUFFIX,liuxiaobo.net\nDOMAIN-SUFFIX,liveleak.com\nDOMAIN-SUFFIX,livestation.com\nDOMAIN-SUFFIX,livestream.com\nDOMAIN-SUFFIX,localpresshk.com\nDOMAIN-SUFFIX,lockerz.com\nDOMAIN-SUFFIX,lolbin.net\nDOMAIN-SUFFIX,loli.net\nDOMAIN-SUFFIX,longhair.hk\nDOMAIN-SUFFIX,lookpic.com\nDOMAIN-SUFFIX,loved.hk\nDOMAIN-SUFFIX,lrip.org\nDOMAIN-SUFFIX,lsd.org.hk\nDOMAIN-SUFFIX,lsj1080.cc\nDOMAIN-SUFFIX,lsj1080.net\nDOMAIN-SUFFIX,lsj1080.tv\nDOMAIN-SUFFIX,ltn.com.tw\nDOMAIN-SUFFIX,lvv2.com\nDOMAIN-SUFFIX,mail-archive.com\nDOMAIN-SUFFIX,maiplus.com\nDOMAIN-SUFFIX,malaymail.com\nDOMAIN-SUFFIX,malaysiakini.com\nDOMAIN-SUFFIX,matters.news\nDOMAIN-SUFFIX,mattwilcox.net\nDOMAIN-SUFFIX,medium.com\nDOMAIN-SUFFIX,mefeedia.com\nDOMAIN-SUFFIX,memehk.com\nDOMAIN-SUFFIX,merit-times.com\nDOMAIN-SUFFIX,merit-times.com.tw\nDOMAIN-SUFFIX,merlinblog.xyz\nDOMAIN-SUFFIX,metrolife.ca\nDOMAIN-SUFFIX,metroradio.com.hk\nDOMAIN-SUFFIX,minghui.org\nDOMAIN-SUFFIX,mingjinglishi.com\nDOMAIN-SUFFIX,mingjingnews.com\nDOMAIN-SUFFIX,mingpaonews.com\nDOMAIN-SUFFIX,mingshengbao.com\nDOMAIN-SUFFIX,minus.com\nDOMAIN-SUFFIX,minzhuzhongguo.org\nDOMAIN-SUFFIX,mirrorbooks.com\nDOMAIN-SUFFIX,mitbbs.com\nDOMAIN-SUFFIX,mkini.net\nDOMAIN-SUFFIX,mlxiaoshuo.com\nDOMAIN-SUFFIX,mobile01.com\nDOMAIN-SUFFIX,mobileways.de\nDOMAIN-SUFFIX,mobypicture.com\nDOMAIN-SUFFIX,moedict.tw\nDOMAIN-SUFFIX,mojim.com\nDOMAIN-SUFFIX,mokeedev.com\nDOMAIN-SUFFIX,molihua.org\nDOMAIN-SUFFIX,mono.ac\nDOMAIN-SUFFIX,mono.sh\nDOMAIN-SUFFIX,moonbbs.com\nDOMAIN-SUFFIX,mozilla.net\nDOMAIN-SUFFIX,mp3ye.eu\nDOMAIN-SUFFIX,mpfinance.com\nDOMAIN-SUFFIX,msguancha.com\nDOMAIN-SUFFIX,mtlmp4.com\nDOMAIN-SUFFIX,myca168.com\nDOMAIN-SUFFIX,mychinanews.com\nDOMAIN-SUFFIX,mycnnews.com\nDOMAIN-SUFFIX,mycould.com\nDOMAIN-SUFFIX,myfreecams.com\nDOMAIN-SUFFIX,myfreshnet.com\nDOMAIN-SUFFIX,myhd1080.tv\nDOMAIN-SUFFIX,myradio.com.hk\nDOMAIN-SUFFIX,myradio.hk\nDOMAIN-SUFFIX,mysinablog.com\nDOMAIN-SUFFIX,nanyang.com\nDOMAIN-SUFFIX,nanyangpost.com\nDOMAIN-SUFFIX,ncchinesenews.com\nDOMAIN-SUFFIX,ndr.de\nDOMAIN-SUFFIX,net1.hkbu.edu.hk\nDOMAIN-SUFFIX,netgear.com\nDOMAIN-SUFFIX,netme.cc\nDOMAIN-SUFFIX,network54.com\nDOMAIN-SUFFIX,networkedblogs.com\nDOMAIN-SUFFIX,newcenturymc.com\nDOMAIN-SUFFIX,newcenturynews.com\nDOMAIN-SUFFIX,newhighlandvision.com\nDOMAIN-SUFFIX,news.hk.msn.com\nDOMAIN-SUFFIX,news.pts.org.tw\nDOMAIN-SUFFIX,newsancai.com\nDOMAIN-SUFFIX,newstapa.org\nDOMAIN-SUFFIX,newtaiwan.com.tw\nDOMAIN-SUFFIX,newtalk.tw\nDOMAIN-SUFFIX,nextdigital.com.hk\nDOMAIN-SUFFIX,nextmag.com.tw\nDOMAIN-SUFFIX,nextmedia.com\nDOMAIN-SUFFIX,ngensis.com\nDOMAIN-SUFFIX,nicovideo.jp\nDOMAIN-SUFFIX,nobel.se\nDOMAIN-SUFFIX,nobelprize.org\nDOMAIN-SUFFIX,notipage.com\nDOMAIN-SUFFIX,nownews.com\nDOMAIN-SUFFIX,nps.gov\nDOMAIN-SUFFIX,nrk.no\nDOMAIN-SUFFIX,ntd.tv\nDOMAIN-SUFFIX,ntdtv.com\nDOMAIN-SUFFIX,ntdtv-dc.com\nDOMAIN-SUFFIX,nuzcom.com\nDOMAIN-SUFFIX,nvquan.org\nDOMAIN-SUFFIX,nyt.com\nDOMAIN-SUFFIX,nytcn.me\nDOMAIN-SUFFIX,nyti.ms\nDOMAIN-SUFFIX,nytimes.com\nDOMAIN-SUFFIX,nytimg.com\nDOMAIN-SUFFIX,nytstyle.com\nDOMAIN-SUFFIX,observechina.net\nDOMAIN-SUFFIX,oclp.hk\nDOMAIN-SUFFIX,ogaoga.org\nDOMAIN-SUFFIX,oiktv.com\nDOMAIN-SUFFIX,olx.com.br\nDOMAIN-SUFFIX,on.cc\nDOMAIN-SUFFIX,onedrive.live.com\nDOMAIN-SUFFIX,onlinestuffs.com\nDOMAIN-SUFFIX,open.com.hk\nDOMAIN-SUFFIX,opendemocracy.net\nDOMAIN-SUFFIX,orientaldaily.com.my\nDOMAIN-SUFFIX,orientaldaily.on.cc\nDOMAIN-SUFFIX,orzhd.com\nDOMAIN-SUFFIX,oursogo.com\nDOMAIN-SUFFIX,oursteps.com.au\nDOMAIN-SUFFIX,outbrain.com\nDOMAIN-SUFFIX,ow.ly\nDOMAIN-SUFFIX,oyax.com\nDOMAIN-SUFFIX,oyou.com.au\nDOMAIN-SUFFIX,page2rss.com\nDOMAIN-SUFFIX,pageflakes.com\nDOMAIN-SUFFIX,panoramio.com\nDOMAIN-SUFFIX,pao-pao.net\nDOMAIN-SUFFIX,paper.li\nDOMAIN-SUFFIX,passiontimes.hk\nDOMAIN-SUFFIX,pbwiki.com\nDOMAIN-SUFFIX,pbworks.com\nDOMAIN-SUFFIX,pcdvd.com.tw\nDOMAIN-SUFFIX,pchome.com.tw\nDOMAIN-SUFFIX,pcij.org\nDOMAIN-SUFFIX,peacehall.com\nDOMAIN-SUFFIX,penchinese.org\nDOMAIN-SUFFIX,peopo.org\nDOMAIN-SUFFIX,perfspot.com\nDOMAIN-SUFFIX,pfd.org.hk\nDOMAIN-SUFFIX,picpar.com\nDOMAIN-SUFFIX,picrar.com\nDOMAIN-SUFFIX,piebridge.me\nDOMAIN-SUFFIX,pimgs.com\nDOMAIN-SUFFIX,pincong.rocks\nDOMAIN-SUFFIX,ping.fm\nDOMAIN-SUFFIX,pinimg.com\nDOMAIN-SUFFIX,pinterest.com\nDOMAIN-SUFFIX,piratescreen.com\nDOMAIN-SUFFIX,piring.com\nDOMAIN-SUFFIX,pixshock.net\nDOMAIN-SUFFIX,playno1.com\nDOMAIN-SUFFIX,playno1.com.tw\nDOMAIN-SUFFIX,popvote.hk\nDOMAIN-SUFFIX,popyard.com\nDOMAIN-SUFFIX,popyard.org\nDOMAIN-SUFFIX,porn.com\nDOMAIN-SUFFIX,pornhub.com\nDOMAIN-SUFFIX,post852.com\nDOMAIN-SUFFIX,posterous.com\nDOMAIN-SUFFIX,potatso.com\nDOMAIN-SUFFIX,potatsocontent.com\nDOMAIN-SUFFIX,powerlinks.com\nDOMAIN-SUFFIX,premeforwindows.com\nDOMAIN-SUFFIX,prestige-av.com\nDOMAIN-SUFFIX,prettyvirgin.com\nDOMAIN-SUFFIX,privoxy.org\nDOMAIN-SUFFIX,pubu.com.tw\nDOMAIN-SUFFIX,qidian.ca\nDOMAIN-SUFFIX,qiwen.lu\nDOMAIN-SUFFIX,quickpornsearch.com\nDOMAIN-SUFFIX,quora.com\nDOMAIN-SUFFIX,quoracdn.net\nDOMAIN-SUFFIX,qxbbs.org\nDOMAIN-SUFFIX,radioaustralia.net.au\nDOMAIN-SUFFIX,ranyunfei.com\nDOMAIN-SUFFIX,rapbull.net\nDOMAIN-SUFFIX,rcinet.ca\nDOMAIN-SUFFIX,readingtimes.com.tw\nDOMAIN-SUFFIX,recovery.org.tw\nDOMAIN-SUFFIX,redchinacn.org\nDOMAIN-SUFFIX,reddit.com\nDOMAIN-SUFFIX,redsquirrel87.com\nDOMAIN-SUFFIX,redtube.com\nDOMAIN-SUFFIX,reduik.com\nDOMAIN-SUFFIX,referer.us\nDOMAIN-SUFFIX,relink.us\nDOMAIN-SUFFIX,rendsmap.com\nDOMAIN-SUFFIX,renminbao.com\nDOMAIN-SUFFIX,resilio.com\nDOMAIN-SUFFIX,restorehk.com\nDOMAIN-SUFFIX,reuters.com\nDOMAIN-SUFFIX,reutersmedia.net\nDOMAIN-SUFFIX,rfa.org\nDOMAIN-SUFFIX,rferl.org\nDOMAIN-SUFFIX,rfi.fr\nDOMAIN-SUFFIX,rfi.my\nDOMAIN-SUFFIX,rghost.net\nDOMAIN-SUFFIX,riku.me\nDOMAIN-SUFFIX,rmjdw.com\nDOMAIN-SUFFIX,rnw.nl\nDOMAIN-SUFFIX,rocmp.org\nDOMAIN-SUFFIX,roodo.com\nDOMAIN-SUFFIX,rsf.org\nDOMAIN-SUFFIX,rsf-chinese.org\nDOMAIN-SUFFIX,rthk.hk\nDOMAIN-SUFFIX,rthk.org.hk\nDOMAIN-SUFFIX,rti.org.tw\nDOMAIN-SUFFIX,s3.amazonaws.com\nDOMAIN-SUFFIX,sadpanda.us\nDOMAIN-SUFFIX,sanmin.com.tw\nDOMAIN-SUFFIX,sanminjiaoliu.net\nDOMAIN-SUFFIX,savemedia.com\nDOMAIN-SUFFIX,savetube.com\nDOMAIN-SUFFIX,savevid.com\nDOMAIN-SUFFIX,saveyoutube.com\nDOMAIN-SUFFIX,scdn.co\nDOMAIN-SUFFIX,scholarism.com\nDOMAIN-SUFFIX,s-dragon.org\nDOMAIN-SUFFIX,search.xxx\nDOMAIN-SUFFIX,secretchina.com\nDOMAIN-SUFFIX,securityinabox.org\nDOMAIN-SUFFIX,securitykiss.com\nDOMAIN-SUFFIX,sendspace.com\nDOMAIN-SUFFIX,setn.com\nDOMAIN-SUFFIX,sex.com\nDOMAIN-SUFFIX,sexinsex.net\nDOMAIN-SUFFIX,shadow.ma\nDOMAIN-SUFFIX,shadowgov.tw\nDOMAIN-SUFFIX,shadowsocks.org\nDOMAIN-SUFFIX,sharenxs.com\nDOMAIN-SUFFIX,sharpdaily.com\nDOMAIN-SUFFIX,sharpdaily.tw\nDOMAIN-SUFFIX,sherrychan.net\nDOMAIN-SUFFIX,shizhao.org\nDOMAIN-SUFFIX,shr.lc\nDOMAIN-SUFFIX,shutterstock.com\nDOMAIN-SUFFIX,sinaapp.co\nDOMAIN-SUFFIX,singtao.com\nDOMAIN-SUFFIX,sinomontreal.ca\nDOMAIN-SUFFIX,sinoquebec.com\nDOMAIN-SUFFIX,sis001.com\nDOMAIN-SUFFIX,sitebro.tw\nDOMAIN-SUFFIX,six-degrees.io\nDOMAIN-SUFFIX,slideshare.net\nDOMAIN-SUFFIX,smh.com.au\nDOMAIN-SUFFIX,smhric.org\nDOMAIN-SUFFIX,softether-download.com\nDOMAIN-SUFFIX,sohcradio.com\nDOMAIN-SUFFIX,sondelespoir.org\nDOMAIN-SUFFIX,sonidodelaesperanza.org\nDOMAIN-SUFFIX,sougouwiki.com\nDOMAIN-SUFFIX,soundofhope.co.kr\nDOMAIN-SUFFIX,soundofhope.kr\nDOMAIN-SUFFIX,soundofhope.org\nDOMAIN-SUFFIX,soup-dev.com\nDOMAIN-SUFFIX,southnews.com.tw\nDOMAIN-SUFFIX,spankwire.com\nDOMAIN-SUFFIX,spotify.com\nDOMAIN-SUFFIX,spring4u.info\nDOMAIN-SUFFIX,ssr.tools\nDOMAIN-SUFFIX,startpage.com\nDOMAIN-SUFFIX,steemit.com\nDOMAIN-SUFFIX,stgloballink.com\nDOMAIN-SUFFIX,stickeraction.com\nDOMAIN-SUFFIX,stimme-de.de\nDOMAIN-SUFFIX,stooorage.com\nDOMAIN-SUFFIX,storify.com\nDOMAIN-SUFFIX,storm.mg\nDOMAIN-SUFFIX,stormmediagroup.com\nDOMAIN-SUFFIX,strongvpn.com\nDOMAIN-SUFFIX,stumbleupon.com\nDOMAIN-SUFFIX,stupidvideos.com\nDOMAIN-SUFFIX,sucuri.net\nDOMAIN-SUFFIX,sugarsync.com\nDOMAIN-SUFFIX,sun1911.com\nDOMAIN-SUFFIX,supersu.com\nDOMAIN-SUFFIX,swagbucks.com\nDOMAIN-SUFFIX,sydneytoday.com\nDOMAIN-SUFFIX,t.co\nDOMAIN-SUFFIX,t.me\nDOMAIN-SUFFIX,t66y.com\nDOMAIN-SUFFIX,taaze.tw\nDOMAIN-SUFFIX,tahr.org.tw\nDOMAIN-SUFFIX,taiwandaily.net\nDOMAIN-SUFFIX,taiwanus.net\nDOMAIN-SUFFIX,talkonly.net\nDOMAIN-SUFFIX,tantannews.com\nDOMAIN-SUFFIX,tavis.tw\nDOMAIN-SUFFIX,tca.org.tw\nDOMAIN-SUFFIX,tdesktop.com\nDOMAIN-SUFFIX,techinasia.com\nDOMAIN-SUFFIX,technorati.com\nDOMAIN-SUFFIX,telegra.ph\nDOMAIN-SUFFIX,telegram.me\nDOMAIN-SUFFIX,telegram.org\nDOMAIN-SUFFIX,telesco.pe\nDOMAIN-SUFFIX,tenacy.com\nDOMAIN-SUFFIX,tenacy-free.com\nDOMAIN-SUFFIX,theblaze.com\nDOMAIN-SUFFIX,thebobs.com\nDOMAIN-SUFFIX,theepochtimes.com\nDOMAIN-SUFFIX,thefrontier.hk\nDOMAIN-SUFFIX,theglobalmail.org\nDOMAIN-SUFFIX,theguardian.com\nDOMAIN-SUFFIX,thehousenews.com\nDOMAIN-SUFFIX,theinitium.com\nDOMAIN-SUFFIX,thenewslens.com\nDOMAIN-SUFFIX,the-sun.on.cc\nDOMAIN-SUFFIX,thetibetpost.com\nDOMAIN-SUFFIX,thetimenow.com\nDOMAIN-SUFFIX,thinkingtaiwan.com\nDOMAIN-SUFFIX,thisav.com\nDOMAIN-SUFFIX,tiananmenduizhi.com\nDOMAIN-SUFFIX,tiananmenmother.org\nDOMAIN-SUFFIX,tiananmenuniv.com\nDOMAIN-SUFFIX,tiananmenuniv.net\nDOMAIN-SUFFIX,tibet.net\nDOMAIN-SUFFIX,time.com\nDOMAIN-SUFFIX,timer-tab.com\nDOMAIN-SUFFIX,tiny.cc\nDOMAIN-SUFFIX,tinychat.com\nDOMAIN-SUFFIX,tmagazine.com\nDOMAIN-SUFFIX,tmu.edu.tw\nDOMAIN-SUFFIX,tokyocn.com\nDOMAIN-SUFFIX,topcoo.com\nDOMAIN-SUFFIX,topsy.com\nDOMAIN-SUFFIX,torrentcrazy.com\nDOMAIN-SUFFIX,torrentkitty.com\nDOMAIN-SUFFIX,trackon.org\nDOMAIN-SUFFIX,transformativeworks.org\nDOMAIN-SUFFIX,transparency.org\nDOMAIN-SUFFIX,trendsmap.com\nDOMAIN-SUFFIX,trib.al\nDOMAIN-SUFFIX,trouw.nl\nDOMAIN-SUFFIX,trtc.com.tw\nDOMAIN-SUFFIX,trueimages.ru\nDOMAIN-SUFFIX,truveo.com\nDOMAIN-SUFFIX,tsquare.tv\nDOMAIN-SUFFIX,tsu.org.tw\nDOMAIN-SUFFIX,tube8.com\nDOMAIN-SUFFIX,tuiyun.net\nDOMAIN-SUFFIX,tumblr.com\nDOMAIN-SUFFIX,tv.com\nDOMAIN-SUFFIX,tvboxnow.com\nDOMAIN-SUFFIX,twbbs.org\nDOMAIN-SUFFIX,twbbs.tw\nDOMAIN-SUFFIX,tweetdeck.com\nDOMAIN-SUFFIX,tweettunnel.com\nDOMAIN-SUFFIX,tweez.net\nDOMAIN-SUFFIX,twgreatdaily.com\nDOMAIN-SUFFIX,twicsy.com\nDOMAIN-SUFFIX,twilk.com\nDOMAIN-SUFFIX,twimg.com\nDOMAIN-SUFFIX,twipple.jp\nDOMAIN-SUFFIX,twitchtv.com\nDOMAIN-SUFFIX,twitiq.com\nDOMAIN-SUFFIX,twitpic.com\nDOMAIN-SUFFIX,twitter.com\nDOMAIN-SUFFIX,twitterfeed.com\nDOMAIN-SUFFIX,twittergadget.com\nDOMAIN-SUFFIX,twitterknowhow.com\nDOMAIN-SUFFIX,twitvid.com\nDOMAIN-SUFFIX,twitvid.com.edgesuite.net\nDOMAIN-SUFFIX,twiyia.com\nDOMAIN-SUFFIX,twtrland.com\nDOMAIN-SUFFIX,ucdc1998.org\nDOMAIN-SUFFIX,udn.com\nDOMAIN-SUFFIX,udn.com.tw\nDOMAIN-SUFFIX,ugo.com\nDOMAIN-SUFFIX,uhrp.org\nDOMAIN-SUFFIX,uighurbiz.net\nDOMAIN-SUFFIX,ulifestyle.com.hk\nDOMAIN-SUFFIX,ultrareach.com\nDOMAIN-SUFFIX,unblock.cn.com\nDOMAIN-SUFFIX,uncyclopedia.tw\nDOMAIN-SUFFIX,upmedia.mg\nDOMAIN-SUFFIX,uproxy.org\nDOMAIN-SUFFIX,ustream.tv\nDOMAIN-SUFFIX,utorrent.com\nDOMAIN-SUFFIX,uwants.com\nDOMAIN-SUFFIX,uyghuramerican.org\nDOMAIN-SUFFIX,uyghurpress.com\nDOMAIN-SUFFIX,uyl.me\nDOMAIN-SUFFIX,v2ex.com\nDOMAIN-SUFFIX,van698.com\nDOMAIN-SUFFIX,vaticanradio.org\nDOMAIN-SUFFIX,vdonext.com\nDOMAIN-SUFFIX,velkaepocha.sk\nDOMAIN-SUFFIX,venchina.com\nDOMAIN-SUFFIX,veoh.com\nDOMAIN-SUFFIX,vevo.com\nDOMAIN-SUFFIX,viber.com\nDOMAIN-SUFFIX,video.pbs.org\nDOMAIN-SUFFIX,vietdaikynguyen.com\nDOMAIN-SUFFIX,vinmusic.com\nDOMAIN-SUFFIX,visiontimes.ca\nDOMAIN-SUFFIX,vjmedia.com.hk\nDOMAIN-SUFFIX,vocus.cc\nDOMAIN-SUFFIX,vot.org\nDOMAIN-SUFFIX,vox-cdn.com\nDOMAIN-SUFFIX,vpngate.jp\nDOMAIN-SUFFIX,vpngate.net\nDOMAIN-SUFFIX,vpnxunlu.com\nDOMAIN-SUFFIX,wahas.com\nDOMAIN-SUFFIX,wanglixiong.com\nDOMAIN-SUFFIX,want-daily.com\nDOMAIN-SUFFIX,watchzerg.com\nDOMAIN-SUFFIX,watorrent.org\nDOMAIN-SUFFIX,wattpad.com\nDOMAIN-SUFFIX,wdf5.com\nDOMAIN-SUFFIX,wearn.com\nDOMAIN-SUFFIX,web.pts.org.tw\nDOMAIN-SUFFIX,weblagu.com\nDOMAIN-SUFFIX,websitepulse.com\nDOMAIN-SUFFIX,webwarper.net\nDOMAIN-SUFFIX,wegfw.com\nDOMAIN-SUFFIX,weibosuite.com\nDOMAIN-SUFFIX,weijingsheng.org\nDOMAIN-SUFFIX,weiquanwang.org\nDOMAIN-SUFFIX,wengewang.org\nDOMAIN-SUFFIX,wenxuecity.com\nDOMAIN-SUFFIX,westca.com\nDOMAIN-SUFFIX,westkit.net\nDOMAIN-SUFFIX,wetransfer.com\nDOMAIN-SUFFIX,whattalking.com\nDOMAIN-SUFFIX,whogovernstw.org\nDOMAIN-SUFFIX,whotalking.com\nDOMAIN-SUFFIX,wi-gadget.com\nDOMAIN-SUFFIX,wikia.com\nDOMAIN-SUFFIX,wikia.net\nDOMAIN-SUFFIX,wikia.nocookie.net\nDOMAIN-SUFFIX,wikia-beacon.com\nDOMAIN-SUFFIX,wiztechnologies.jp\nDOMAIN-SUFFIX,wn.com\nDOMAIN-SUFFIX,worldcat.org\nDOMAIN-SUFFIX,worldjournal.com\nDOMAIN-SUFFIX,wrchina.org\nDOMAIN-SUFFIX,wretch.cc\nDOMAIN-SUFFIX,wuala.com\nDOMAIN-SUFFIX,wuerkaixi.com\nDOMAIN-SUFFIX,wujie.net\nDOMAIN-SUFFIX,wujieliulan.com\nDOMAIN-SUFFIX,wwitv.com\nDOMAIN-SUFFIX,xanga.com\nDOMAIN-SUFFIX,xbookcn.net\nDOMAIN-SUFFIX,xhamster.com\nDOMAIN-SUFFIX,xiaochuncnjp.com\nDOMAIN-SUFFIX,xinmiao.com.hk\nDOMAIN-SUFFIX,xinyubbs.net\nDOMAIN-SUFFIX,xiti.com\nDOMAIN-SUFFIX,xizang-zhiye.org\nDOMAIN-SUFFIX,xjp.cc\nDOMAIN-SUFFIX,xn--fiq681d48s.org\nDOMAIN-SUFFIX,xpics.us\nDOMAIN-SUFFIX,xtube.com\nDOMAIN-SUFFIX,xuehua.us\nDOMAIN-SUFFIX,xunluvpn.com\nDOMAIN-SUFFIX,xvideosmatome.net\nDOMAIN-SUFFIX,xys.org\nDOMAIN-SUFFIX,yamedia.tw\nDOMAIN-SUFFIX,yasni.co.uk\nDOMAIN-SUFFIX,ycp.hk\nDOMAIN-SUFFIX,yfrog.com\nDOMAIN-SUFFIX,yhritw.org\nDOMAIN-SUFFIX,yibaochina.com\nDOMAIN-SUFFIX,yidio.com\nDOMAIN-SUFFIX,yorkbbs.ca\nDOMAIN-SUFFIX,you2go.me\nDOMAIN-SUFFIX,youjizz.com\nDOMAIN-SUFFIX,youmaker.com\nDOMAIN-SUFFIX,youporn.com\nDOMAIN-SUFFIX,youthwant.com.tw\nDOMAIN-SUFFIX,youtu.be\nDOMAIN-SUFFIX,youtube.com\nDOMAIN-SUFFIX,youtubeinmp3.com\nDOMAIN-SUFFIX,ytimg.com\nDOMAIN-SUFFIX,yzzk.com\nDOMAIN-SUFFIX,zhengjian.org\nDOMAIN-SUFFIX,zhenlibu1984.com\nDOMAIN-SUFFIX,zhongzilou.com\nDOMAIN-SUFFIX,zhoushuguang.com\nDOMAIN-SUFFIX,zinio.com\nDOMAIN-SUFFIX,zlvc.net\nDOMAIN-SUFFIX,zlvc.net.he2.aqb.so\nDOMAIN-SUFFIX,zomobo.net\nDOMAIN-SUFFIX,zorpia.com\nDOMAIN-SUFFIX,zuo.la\nDOMAIN-SUFFIX,zuobiao.me\nDOMAIN-SUFFIX,zuola.com\nDOMAIN-SUFFIX,zxing.org\nDOMAIN-SUFFIX,zyzc.greatzhonghua.org\n"
  },
  {
    "path": "Enable_BoringSSL_OCSP.patch",
    "content": "From: CarterLi <carter.li@eoitek.com>\nDate: Sat, 19 May 2018 22:08:47 +0800\nSubject: [PATCH] Support OSCP stapling on BoringSSL\nLink: https://github.com/kn007/patch/issues/4\nModified: kn007\n\n\ndiff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c\nindex 0bea5e7..334f1c2 100644\n--- a/src/event/ngx_event_openssl_stapling.c\n+++ b/src/event/ngx_event_openssl_stapling.c\n@@ -1874,8 +1874,50 @@ ngx_int_t\n ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,\n     ngx_str_t *responder, ngx_uint_t verify)\n {\n+#ifdef BORINGSSL_MAKE_DELETER\n+    ngx_log_error(NGX_LOG_NOTICE, ssl->log, 0,\n+                  \"using boringssl, currently only \\\"ssl_stapling_file\\\" is supported. use it as your own risk\");\n+\n+    BIO            *bio;\n+    int             len;\n+    u_char          buf[2048];\n+\n+    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {\n+        return NGX_ERROR;\n+    }\n+\n+    bio = BIO_new_file((char *) file->data, \"r\");\n+    if (bio == NULL) {\n+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n+                      \"BIO_new_file(\\\"%s\\\") failed\", file->data);\n+        return NGX_ERROR;\n+    }\n+\n+    len = BIO_read(bio, buf, sizeof(buf) / sizeof(u_char));\n+    BIO_free(bio);\n+    bio = NULL;\n+\n+    if (len <= 0) {\n+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n+                      \"Read OCSP response file \\\"%s\\\" failed: %d\", file->data, len);\n+        return NGX_ERROR;\n+    }\n+\n+    if (len >= 2000) {\n+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n+                      \"Unexpected OCSP response file length: %d\", len);\n+        return NGX_ERROR;\n+    }\n+\n+    if (!SSL_CTX_set_ocsp_response(ssl->ctx, buf, len)) {\n+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n+                      \"SSL_CTX_set_ocsp_response(ssl->ctx, buf, %d) failed\", len);\n+        return NGX_ERROR;\n+    }\n+#else\n     ngx_log_error(NGX_LOG_WARN, ssl->log, 0,\n                   \"\\\"ssl_stapling\\\" ignored, not supported\");\n+#endif\n \n     return NGX_OK;\n }\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Karl Chen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Patches\n\n## Nginx\n\n### use_openssl_md5_sha1.patch\n* Use the OpenSSL library instead of the Nginx original function.\n* Repack it because \"patch unexpectedly ends in middle of line\".\n    - Thanks [@CarterLi](https://github.com/kn007/patch/issues/5)\n\nTest pass: 1.29.2\n\n### nginx_dynamic_tls_records.patch\n* Add Dynamic TLS Record Support.\n\nRequire: Nginx 1.29.2\n\nTest pass: 1.29.2\n\n### Enable_BoringSSL_OCSP.patch\n* For BoringSSL support OCSP stapling.\n    - Using \"ssl_stapling_file\" to support.\n    - Only \"ssl_stapling_file\" with single cert is supported.\n    - Auto-rebuild OCSP stapling file with shell and atd(at cron), you can read this [article](https://kn007.net/topics/let-nginx-support-ocsp-stapling-when-using-boringssl/)(Maybe you need a translation tool).\n    - Thanks [@CarterLi](https://github.com/kn007/patch/issues/4).\n\nTest pass: 1.25.5\n\n### nginx.patch (Discontinued)\n* Add HTTP2 HPACK Encoding Support.\n* Add Dynamic TLS Record Support.\n\nRequire: Nginx 1.25.0 (this version only)\n\nTest pass: 1.25.0\n\nSince `Nginx` 1.25.1, HPACK encoding will not support because the HTTP/2 server push support has been removed.\n\n### nginx_with_quic.patch (Discontinued)\n* Add HTTP3(QUIC) Support.\n    - For OCSP stapling, maybe you need [this](https://github.com/kn007/patch#enable_boringssl_ocsppatch).\n* Add HTTP2 HPACK Encoding Support.\n* Add Dynamic TLS Record Support.\n\nRequire: Nginx 1.21.4 or later.\n\nTest pass: 1.23.3 with [cloudflare/quiche@c9311a1](https://github.com/cloudflare/quiche/tree/c9311a18910c0277867c34c0acc4a9711b50b913)\n\n<b>Check your modules when build failed.</b>\n\n### nginx_for_1.23.4.patch (Deprecated)\n* Add HTTP2 HPACK Encoding Support.\n* Add Dynamic TLS Record Support.\n\nRequire: Nginx version below 1.25.0\n\nTest pass: 1.23.4\n\n### nginx_with_quic_for_1.19.7_full.patch (Deprecated)\n* Add HTTP3(QUIC) Support.\n    - For OCSP stapling, maybe you need [this](https://github.com/kn007/patch#enable_boringssl_ocsppatch).\n* Add HTTP2 HPACK Encoding Support.\n* Add Dynamic TLS Record Support.\n\nRequire: Nginx 1.19.7 or later(below 1.21.4).\n\nTest pass: 1.21.3 with [cloudflare/quiche@af1bbc0](https://github.com/cloudflare/quiche/tree/af1bbc03e9992bae516d0b692a481de64bd4e8d9)\n\n`nginx_with_quic_for_1.19.6.patch` is required to support Nginx versions lower than 1.19.7, cause `post_accept_timeout` had been removed by Nginx since 1.19.7.\n\n### nginx_with_quic_for_1.19.6.patch (Deprecated)\n* Revert `nginx_with_quic.patch` to support Nginx versions lower than 1.19.7.\n* Patch `nginx_with_quic.patch` first, then patch this one.\n\nTest pass: 1.19.6 with [nginx_with_quic.patch@ec8cac4](https://github.com/kn007/patch/blob/ec8cac4fc74b1718e9b005e7533201aec552aa40/nginx_with_quic.patch) & [cloudflare/quiche@fca5e9a](https://github.com/cloudflare/quiche/tree/fca5e9acdfdff9e80c7b9346214c64b393108328)\n\n### nginx_with_spdy.patch (Deprecated)\n* Add SPDY Support.\n* Add HTTP2 HPACK Encoding Support.\n* Add Dynamic TLS Record Support.\n\nTest pass: 1.17.9\n\n### nginx_with_spdy_quic.patch (Deprecated)\n* Add SPDY Support.\n* Add HTTP3(QUIC) Support.\n* Add HTTP2 HPACK Encoding Support.\n* Add Dynamic TLS Record Support.\n\nTest pass: 1.17.9 with [cloudflare/quiche@9a8b3b](https://github.com/cloudflare/quiche/tree/9a8b3b12d007715cd4cc254362db51d5a01de9f2)\n\n## Other\n\n### openssl-1.1.1.patch\n* Add TLS 1.3 Support.\n* Add BoringSSL's Equal Preference Support.\n* Add ChaCha20-Poly1305 Draft Version Support.\n\nTest pass: 1.1.1w\n\n### ffmpeg-let-rtmp-flv-support-hevc-h265-opus.patch\n* FLV/RTMP Extensions For FFmpeg.\n    - Add FLV Encode/Decode with H.265/HEVC & OPUS Codec Support.\n    - Add RTMP Stream Push with H.265/HEVC & OPUS Codec Support.\n    - Thanks [@xia-chu](https://github.com/xia-chu/ZLMediaKit/wiki/RTMP%E5%AF%B9H265%E5%92%8COPUS%E7%9A%84%E6%94%AF%E6%8C%81).\n\nTest pass: 4.3.1\n\n### dropbox_fs_fix.patch\n* For Dropbox Linux users. This patch could let official python script auto-load `libdropbox_fs_fix.so` library before start dropboxd.\n    - Using [Dropbox filesystem fix for Linux Repo](https://github.com/dark/dropbox-filesystem-fix) and make `libdropbox_fs_fix.so`.\n    - After compiled, copy `libdropbox_fs_fix.so` to `$HOME/.dropbox-dist/libdropbox_fs_fix.so`.\n    - Download Dropbox official python script, put it with patch file together.\n    - Patch, enjoy.\n\nTest pass: 2019.02.14 version\n\n## Links\n[聊聊Nginx 1.25和HTTP/3](https://kn007.net/topics/talk-about-nginx-1-25-and-http-3/)\n\n[Nginx 1.19.4新特性推荐](https://kn007.net/topics/nginx-1-19-4-new-feature-recommendation/)\n\n[让Nginx使用BoringSSL时支持OCSP Stapling](https://kn007.net/topics/let-nginx-support-ocsp-stapling-when-using-boringssl/)\n\n[博客终止使用TLS 1.0和TLS 1.1协议](https://kn007.net/topics/deprecating-tls-1-0-and-tls-1-1-protocols/)\n\n[小试HTTP3](https://kn007.net/topics/try-http3/)\n\n[我的Nginx编译之旅](https://kn007.net/topics/my-nginx-compilation-tour/)\n\n[解决Dropbox Linux客户端因文件系统导致无法同步问题](https://kn007.net/topics/fix-dropbox-filesystem-sync-problem-for-linux-client/)\n\n[kn007的个人博客](https://kn007.net) \n"
  },
  {
    "path": "blacklist.dgjy",
    "content": "dmad.info\npin.hamturer.com\nsim.miniast.com\ntongjii.us\nslushpool.com\nmlmy.3322.org\nfget-career.com\n3666777a.com\ngp.like383.com\ntj.gogo2021.xyz\nwebmine.cz\narthur.niria.biz\napple-pie.in\nalthawry.org\nwww.careerdesk.org\nwww.hiprofitnetworks.com\nnvhaaa.top\nttzytp.com\nxiusebf1.com\nhpdwfd2.com\nrfyqtv2.com\nqbyyvg3.com\nkqvkvc3.com\nbfrmye5.com\nfpvdxd5.com\nmjrvkv5.com\nnrxduw5.com\nrzgvdm5.com\nupffxs6.com\nexwytd7.com\ndfwskw7.com\npvhgws7.com\ngezkdx7.com\nqczuqw8.com\nkupfkc9.com\nrrtwda9.com\nunpfqc9.com\nahmediye.net\npark.realbig.online\na.51qingmiao.com.kaxi.net\nwxy398.com\nxred.mooo.com\ngp.miaoxia123.com\ndz.qd388.cn\ntyszlga.wweczyc.com\nlrepacks.net\nimg.yparse.com\namsamex.com\njsecoin.com\nup.2nike.org\ng2.arrowhitech.com\nampyazilim.com.tr\nscriptcc.cc\nbeibeixixi.f3322.org\nok2.lovehy.com\ntp1jcgl644jk.com\ns6_down.listw.top\ns6_req.listw.top\nz100.vip\njxn0.51ginkgo.com\nwww.zigui.org\ninit.icloud-analysis.com\nftp.byethost7.com\nftp.byethost10.com\nfiles.000webhost.com\n000webhostapp.com\na7788.1apps.com\nattach10132.1apps.com\nbluemountain.1apps.com\nfiler1.1apps.com\ns8877.1apps.com\nugig.ir\nceye.io\nshare.dmca.gripe\n9bys.com\namsamex.com\njwhss.com\nprothid.ny.us.gamesurge.net\ngameservers.nj.us.gamesurge.net\napi.ceye.io\nipfs.via0.com\nthemoviehometheather.com\ngcdz.info\nbrontocdn.com\nvia0.com\n765567.xyz\n7521.com\ngo2cliks.net\nelectladyproductions.com\nhaifti.com\nwtkbxx.com\ntesyk.com\ntesjj.com\ntesiu.com\nsaxyit.com\nxx-x.catdggga.com\ncatdggga.com\nexitgames.com\navdog-l0597.vip\navdog.net\ncdnupdateservice.com\n9988948.com\nmmo2350.top\ngb.dyabgjaf.com\nshunyi520.vicp.net\nji.kunkunonline.top\nmycontrolpanel29.com\ndasan.one\niluearv.net\nysuw.xyz\nudxuke.net\ndh.alyun.cn\nuiwixv.net\niuayeu.net\ndark-confusion.com\nzerostdio.com\niachks.net\ncontabostorage.com\ncacen.despacito5.com\n"
  },
  {
    "path": "bypass.list",
    "content": "[SwitchyOmega Conditions]\n; Require: SwitchyOmega >= 2.3.2\n; Date: 2026/3/28\n; Usage: https://github.com/FelisCatus/SwitchyOmega/wiki/RuleListUsage\n\n; https://raw.githubusercontent.com/kn007/patch/master/bypass.list\n\n*.c.gle\n*.wallhaven.cc\n*.manhuagui.com\n*.catbox.moe\n*.mixtape.moe\n*.maxroll.gg\n*.simaware.ca\n*.arcgisonline.com\n*.rainviewer.com\nsubscriptionsmanagement-pa.googleapis.com\nsubscriptionsmobile-pa.googleapis.com\nphosphor-pa.googleapis.com\ngrowth-pa.googleapis.com\n*.csb.app\n*.codesandbox.io\n*.cloud.cupronickel.goog\n*.openai.com\n*.twitch.tv\n*.bose.com\n*.worldofwarships.asia\n*.worldofwarships.jp\n*.wargaming.net\n*.hikarifield.co.jp\n*.postfix.org\n*.ietf.org\n*.schema.org\n*.youtu.be\n*.doubleclick.net\n*.googleadservices.com\n*.google.cn\n*.googleapps.com\n*.thinkwithgoogle.com\n*.withgoogle.com\n*.fbcdn.net\n*.flickr.com\n*.youtube-nocookie.com\n*.youtube.com\n*.googleusercontent.com\n*.thepiratebay.se\n*.gmail.com\n*.google.com*\n*.facebook.com\n*.vimeo.com\n*.twitter.com\n*.staticflickr.com\n*.sstatic.net\n*.imgur.com\n*.fastly.net\n*.bit.ly\n*.newrelic.com\n*.paypal.com\n*.paypalobjects.com\n*.cloudfront.net\n*.googlepages.com\n*.twimg.com\n*.t.co\n*.ytimg.com\n*.adzerk.net\n*.xmages.net\n*.fucklo.li\n*.freeproxylists.net\n*.archive.org\n*.shadowsocks.org\n*.dropbox.com\n*.blogspot.com\n*.rage4.com\n*.brandonchecketts.com\n*.googleratings.com\n*.letscorp.net\n*.googlevideo.com\n*.gravatar.com\n*.goods-pro.com\n*.w.org\n*.wp.com\n*.deviantart.com\n*.deviantart.net\n*.archive.fo\n*.blogger.com\n*.wordpress.com\n*.instagram.com\n*.facebook.net\n*.github.io\n*.github.com\n*.githubusercontent.com\n*.ssl-images-amazon.com\n*.associates-amazon.com\n*.media-amazon.com\n*.awsstatic.com\n*.googlecode.com\n*.live.com\n*.onedriver.com\n*.wikipedia.org\n*.wikimedia.org\n*.nodequery.com\n*.androidfilehost.com\n*.appspot.com\n*.pastebin.com\n*.dropboxstatic.com\n*.disq.us\n*.disquscdn.com\n*.disqus.com\n*.alexa.com\n*.amazonaws.com\n*.bbc.co.uk\n*.travis-ci.org\n*.*.google\n*.goog\n*.pki.goog\n*.cdninstagram.com\n*.wordpress.org\n*.slideshare.net\n*.textnow.com\n*.gfx.ms\n*.chromium.org\n*.goo.gl\n*.googleblog.com\n*.wikileaks.org\n*.google.co.*\n*.ggpht.com\n*.t.me\n*.telegram.me\n*.telegra.ph\n*.telegram.org\n*.tdesktop.com\n*.cisco.com\n*.oneplus.net\n*.g.co\n*.amazon.com\n*.amazon.co.*\n*.xda-developers.com\n*.mysql.com\n*.tiny.cc\n*.speedyrails.net\n*.androidfilehost.com\n*.pinimg.com\n*.flightradar24.com\n*.dropboxusercontent.com\n*.cloudconvert.com\n*.steaminventoryhelper.com\n*.steamcardexchange.net\n*.steamcommunity.com\n*.steampowered.com\n*.steam-api.com\n*.akamaihd.net\n*.keycdn.com\n*.pumpcloud.net\n*.mega.nz\n*.mega.co.nz\n*.telesco.pe\n*.discordapp.net\n*.discordapp.com\n*.discord.com\n*.discord.gg\n*.redd.it\n*.reddit.com\n*.redditstatic.com\n*.redditmedia.com\n*.rawgit.com\n*.ampproject.net\n*.windy.com\n*.githubassets.com\n*.bootstrapcdn.com\n*.ubi.com\n*.ubisoft.com\n*.wonderpush.com\n*.goo.gl\n*.similarweb.com\n*.similarcdn.com\n*.intercom.io\n*.intercomcdn.com\n*.regex101.com\n*.jsfiddle.net\n*.jshell.net\n*.tunemymusic.com\n*.unsplash.com\n*.ping.pe\n*.spotify.com\n*.scdn.co\n*.3dmark.com\n*.futuremark.com\n*.ultravps.eu\n*.providerdienste.de\n*.virtualhosts.de\n*.he.net\n*.akamaized.net\n*.typekit.net\n*.akamaihd.net\n*.jquery.com\n*.ubi.li\n*.github.blog\n*.steamdb.info\n*.algolia.net\n*.steamstatic.com\n*.pixiv.net\n*.pximg.net\n*.pixnet.net\n*.pixfs.net\n*.sharemods.com\n*.notion.so\n*.imgbox.com\n*.geeks3d.com\n*.nvidia.com\n*.gamepadviewer.com\n*.humansofnewyork.com\n*.cloudflare.com\n*.wagnardsoft.com\n*.gitlab.com\n*.gitlab-static.net\n*.soundcloud.com\n*.sndcdn.com\n*.dl.sourceforge.net\n*.javmost.com\n*.javtrust.com\n*.jable.tv\n*.dmm.co.jp\n*.tomyangsh.pw\n*.rouman5.com\n*.rou.video\n*.htpt.cc\n*.soulvoice.club\n*.m-team.cc\n*.m-team.io\n*.hdcmct.org\n*.springsunday.net\n*.pterclub.com\n*.totheglory.im\n*.imgurl.org\n*.flycrow.pro\n*.groueta.cc\n*.mantou.biz\n*.ccache.org\n*.seejav.bid\n*.picturedata.org\n*.javbee.net\n*.bmp.ovh\n*.open.cd\n*.dmhy.org\n*.keepfrds.com\n*.jdbimgs.com\n*.gifyu.com\n*.dmmsee.fun\n*.bitvise.com\n*.chucklefish.org\n*.steamgames.com\n*.steamcontent.com\n*.steamusercontent.com\n*.fast.com\n*.netflix.com\n*.nflxvideo.net\n*.nflxext.com\n*.nflxso.net\n*.onetrust.com\n*.fosshub.com\n*.autodesk.com.cn\n*.autodesk.com\n*.autodesk.net\n*.openvpn.net\n*.webflow.io\n*.webflow.com\n*.pstorage.space\n*.netcdn.space\n*.boost.org\n*.tarolink.top\n*.o--o.xyz\n*.simgbb.com\n*.imgbb.com\n*.ibb.co\n*.z4a.net\n*.iili.io\n*.weserv.nl\n*.shoot.photo\n*.ccp.ovh\n*.imagebam.com\n*.truenas.com\n*.themoneyconverter.com\n*.jpopsuki.eu\n*.skyvector.com\n*.postimgs.org\n*.postlmg.cc\n*.imagecurl.com\n*.simbrief.com\n*.flightsim.to\n*.flybywiresim.com\n*.chartfox.org\n*.openstreetmap.org\n*.vatsim.net\n*.navigraph.com\n*.gog-statics.com\n*.gog.com\n*.zendesk.com\n*.zdassets.com\n*.avsim.com\n*.sda1.dev\n*.sl.al\n*.ccimg.xyz\n*.admod.com\n*.web.dev\n*.jsdelivr.net\n*.jsdelivr.com\n*.evga.com\n*.imageshack.com\n*.adultempire.com\n*.exoticaz.to\n*.storages.cc\n*.rockstargames.com\n*.arkoselabs.com\n*.arkoselabs.cn\n*.javhdporn.net\n*.i18n.pw\n*.greasyfork.org\n*.qbittorrent.org\n*.seejav.bid\n*.jdbstatic.com\n*.021jf.com\n*.pic599.net\n*.98tuch.net\n*.postto.me\n*.aspnetcdn.com\n*.netlify.app\n*.netlify.com\n*.beautifyconverter.com\n*.healthchecks.io\n*.mgstage.com\n*.gvt2.com\n*.gstatic.com\n*.googleapis.com\n*.gmodules.com\n*.sesexiaozhan.com\n*.biquge.tw\n*.acgnx.se\n*.combot.org\n*.tenor.com\n*.pixeldrain.com\n*.gamer.com.tw\n*.bahamut.com.tw\n*.seiya-saiga.com\n*.ptpimg.me\n*.wiki.gg\nHostRegex: ^.*\\brmcdn\\d+\\.xyz$\nHostRegex: ^.*\\brn\\d+\\.xyz$\nHostRegex: ^.*\\broum\\d+\\.xyz$\n\n; ==============================================================================\n; ==============================================================================\n; https://github.com/xinhugo/Free-List\n\nHostRegex: ^(sp|www|actress|pics)\\.dmm\\.co\\.jp$\nwww.dmm.com\nwww.r18.com\n\n*.1024search.tk\n*.1080.tw\n*.1688.com.au\n*.1dpw.com\n*.1pondo.tv\n*.2008xianzhang.info\n*.24smile.org\n*.4shared.com\n*.5i01.com\n*.5z5.com\n*.64memo.com\n*.64tianwang.com\n*.64wiki.com\n*.666kb.com\n*.6do.news\n*.6park.com\n*.a1080hd.com\n*.abc.xyz\n*.ablwang.com\n*.aboluowang.com\n*.actimes.com.au\n*.adblockplus.org\n*.adobe.com\n*.adsafeprotected.com\n*.adsrvr.org\n*.adultblogranking.com\n*.aforcemorepowerful.org\n*.ahd1080.com\n*.aida64.com\n*.aisex.com\n*.aishangyou.tube\n*.ait.org.tw\n*.akamaihd.net\n*.alabout.com\n*.alicejapan.co.jp\n*.aliengu.com\n*.alliance.org.hk\n*.allinfa.com\n*.am730.com.hk\n*.amazon.co.jp\n*.amazonaws.com\n*.amazonwebapps.com\n*.amnesty.org\n*.amnesty.tw\n*.ananass.fr\n*.android.com\n*.androidfilehost.com\n*.animecrazy.net\n*.anti1984.com\n*.anygoing.com\n*.aomiwang.com\n*.aoqinet.com\n*.apkmirror.com\n*.app2.hkatv.com\n*.appledaily.hk\n*.appspot.com\n*.archive.org\n*.asahichinese.com\n*.asianews.it\n*.aszw.com\n*.atchinese.com\n*.atdmt.com\n*.atgfw.org\n*.aurl.mobi\n*.ausdaily.net.au\n*.autoit-cdn.com\n*.autoitscript.com\n*.avbbs.tv\n*.avcity.tv\n*.avlang.com\n*.av-scouter.info\n*.avsp2p.com\n*.backchina.com\n*.backtotiananmen.com\n*.badongo.com\n*.baisex.me\n*.bannedbook.org\n*.bayfiles.net\n*.bbcchinese.com\n*.bbg.gov\n*.bcchinese.net\n*.beijingspring.com\n*.bet365.com\n*.betanews.com\n*.beyondfirewall.com\n*.bind9.net\n*.binux.me\n*.bit.ly\n*.bitshare.com\n*.bitsnoop.com\n*.biz.tm\n*.bjs.org\n*.bjzc.org\n*.blinkx.com\n*.blockedinchina.net\n*.blog.jp\n*.blog.xuite.net\n*.blog.yam.com\n*.blogblog.com\n*.blogcatalog.com\n*.blogcity.me\n*.blogimg.jp\n*.bloglines.com\n*.bloglovin.com\n*.blogs.com\n*.blogspot.in\n*.blogspot.kr\n*.bmvflorida.us\n*.bod.asia\n*.book.com.tw\n*.books.com.tw\n*.bootstrapcdn.com\n*.botanwang.com\n*.botanwang.org\n*.bowenpress.com\n*.boxun.com\n*.boxun.us\n*.break.com\n*.brizzly.com\n*.btdigg.org\n*.btku.org\n*.btn.weather.ca\n*.bud.org.tw\n*.buff.ly\n*.bullog.org\n*.bullogger.com\n*.businessweek.com\n*.bwp.im\n*.bx.tl\n*.c.bigcache.googleapis.com\n*.c000.me\n*.c080.me\n*.c800.me\n*.c9x.info\n*.cahr.org.tw\n*.campaign.tw-npo.org\n*.cams.org.sg\n*.canyu.org\n*.caochangqing.com\n*.cap.org.hk\n*.cari.com.my\n*.caribbeancom.com\n*.catholic.org.hk\n*.catholic.org.tw\n*.catwizard.net\n*.cbc.ca\n*.ccdtr.org\n*.ccim.org\n*.ccw.org.tw\n*.cdef.org\n*.cdjp.org\n*.cdnews.com.tw\n*.cdns.com.tw\n*.cecc.gov\n*.cenews.eu\n*.centralnation.com\n*.cfhks.org.hk\n*.cgdepot.org\n*.chicagoncmtv.com\n*.china.ucanews.com\n*.chinaaid.net\n*.chinabiz.org.tw\n*.chinadigitaltimes.net\n*.chinaelections.com\n*.chinaelections.org\n*.chinaeweekly.com\n*.chinafile.com\n*.chinagfw.org\n*.chinainperspective.com\n*.chinapress.com.my\n*.chinarightsia.org\n*.chinatcc.gov.cn\n*.chinatimes.com\n*.china-week.com\n*.chinaworker.info\n*.chinesedaily.com\n*.chinesenewsnet.com\n*.chinesepen.org\n*.chinesetoday.com\n*.chosun.com\n*.christianstudy.com\n*.christiantimes.org.hk\n*.chrlawyers.hk\n*.chrome.com\n*.chromium.org\n*.chubun.com\n*.cincainews.com\n*.citizenlab.org\n*.citizensradio.org\n*.city9x.com\n*.civicparty.hk\n*.civilhrfront.org\n*.civilmedia.tw\n*.ck101.com\n*.ckcdn.com\n*.clickme.net\n*.cloudmonitor.ca.com\n*.cna.com.tw\n*.cnd.org\n*.cnliberals.com\n*.cnn.com\n*.cnpolitics.org\n*.cnyes.com\n*.codeproject.com\n*.comefromchina.com\n*.competitionforce.hk\n*.cool18.com\n*.coolloud.org.tw\n*.cotweet.com\n*.cpuid.com\n*.crazys.cc\n*.creaders.net\n*.creadersnet.com\n*.crwdcntrl.net\n*.crystalmark.info\n*.c--spanarchives.-org\n*.c-spanvideo.org\n*.cts.com.tw\n*.cw.com.tw\n*.d100.net\n*.d2pass.com\n*.dadazim.com\n*.dailymotion.com\n*.dalailamaworld.com\n*.danwei.org\n*.daolan.net\n*.dbanotes.net\n*.del.icio.us\n*.democraticchina.org\n*.democrats.org\n*.de-sci.org\n*.dfs.kuaipan.cn\n*.dfzdaili.com\n*.dieneueepoche.com\n*.digg.com\n*.digitalocean.com\n*.digitalvolcano.co.uk\n*.diigo.com\n*.dipity.com\n*.discuss.com.hk\n*.disp.cc\n*.disqus.com\n*.djjsq.com\n*.djorz.com\n*.dnscrypt.org\n*.doit.im\n*.dolc.de\n*.dolf.org.hk\n*.dongtaiwang.com\n*.doub.io\n*.doubibackup.com\n*.download.aircrack-ng.org\n*.dphk.org\n*.dpp.org.tw\n*.dropbox.com\n*.dropboxusercontent.com\n*.drsunacademy.com\n*.dtiblog.com\n*.duga.jp\n*.duihua.org\n*.duping.net\n*.dupola.com\n*.dw.com\n*.dw.de\n*.dw-world.com\n*.dw-world.de\n*.dxlive.com\n*.e123.hk\n*.ebookbrowse.com\n*.ecfa.org.tw\n*.echinanews.com.tw\n*.edge.liveleak.com\n*.edicypages.com\n*.edoors.com\n*.efcc.org.hk\n*.eff.org\n*.eic-av.com\n*.e-info.org.tw\n*.elpais.com\n*.emilylau.org.hk\n*.erabaru.net\n*.erights.net\n*.eroantenna.com\n*.ero-video.net\n*.es-visiontimes.com\n*.etaiwannews.com\n*.ettoday.net\n*.evernote.com\n*.extremetube.com\n*.extremetube.phncdn.com\n*.eyny.com\n*.facebook.com\n*.facebook.net\n*.fangeming.com\n*.fanqiang.network\n*.fanqianghou.com\n*.farxian.com\n*.fastly.net\n*.faststone.org\n*.favstar.fm\n*.faydao.com\n*.fb.me\n*.fbcdn.net\n*.fc2.com\n*.fdc89.jp\n*.feedburner.com\n*.feedjit.com\n*.feedsportal.com\n*.felixcat.net\n*.ffx.io\n*.filecroco.com\n*.filesor.com\n*.file-static.com\n*.filestube.com\n*.firebaseio.com\n*.firepic.org\n*.flyzy2005.com\n*.fmnnow.com\n*.foofind.is\n*.fooooo.com\n*.forum.kaiyuan.de\n*.forum.tvb.com\n*.fqrouter.com\n*.free4u.com.ar\n*.freebrowser.org\n*.freedomhouse.org\n*.freeproxyserver.net\n*.freeshadow.info\n*.freetufu.com\n*.freewechat.com\n*.freeweibo.com\n*.friendfeed.com\n*.fring.com\n*.frontlinedefenders.org\n*.ftchinese.com\n*.fukugan.com\n*.fuli.ba\n*.fullyillustrated.com\n*.fungchiwood.com\n*.funp.com*\n*.fw.cm\n*.fw.com\n*.g.co\n*.gaeproxy.com\n*.gamebase.com.tw\n*.gameclub.tw\n*.ganges.com\n*.gcpnews.com\n*.geocities.co.jp\n*.getfoxyproxy.org\n*.getgom.com\n*.getsync.com\n*.ggpht.com\n*.ghd1080.com\n*.gigacircle.com\n*.gimp.org\n*.git.io\n*.github.com\n*.github.io\n*.githubusercontent.com\n*.git-scm.com\n*.gittigidiyor.com\n*.globalvoices.org\n*.globalvoicesonline.org\n*.glorystar.me\n*.gmail.com\n*.gnews.org\n*.goagent.biz\n*.goo.gl\n*.google\n*.google.com\n*.googlecode.com\n*.googlesource.com\n*.googletagmanager.com\n*.googleusercontent.com\n*.googlevideo.com\n*.gopetition.com\n*.gospelherald.com\n*.gospelherald.com.hk\n*.gov.tw\n*.gpass1.com\n*.gravatar.com\n*.greatfire.org\n*.greatfirewallofchina.org\n*.greatzhonghua.org\n*.greenparty.org.tw\n*.gvm.com.tw\n*.h528.com\n*.haixiainfo.com.tw\n*.hakkatv.org.tw\n*.hav.tv\n*.have8.com\n*.h-china.org\n*.hcocoa.com\n*.hd1080.org\n*.hdtransform.com\n*.hechaji.com\n*.helpzhuling.org\n*.heqinglian.net\n*.hetnieuwetijdperk.com\n*.hexieshe.com\n*.hikinggfw.org\n*.hilive.tv\n*.hioz.org\n*.hitwister.com\n*.hjav.org\n*.hjclub.info\n*.hk01.com\n*.hkatvnews.com\n*.hkchurch.org\n*.hkci.org.hk\n*.hkcnews.com\n*.hkcrm.org.hk\n*.hkdailynews.com.hk\n*.hkdash.com\n*.hkej.com\n*.hket.com\n*.hketgroup.com\n*.hkgolden.com\n*.hkgoldenmobile.com\n*.hkhkhk.com\n*.hkhrm.org.hk\n*.hkja.org.hk\n*.hkjc.com\n*.hkjp.org\n*.hkptu.org\n*.hk-pub.com\n*.hkreporter.com\n*.hksilicon.com\n*.hkumall.com\n*.hkupop.hku.hk\n*.hkusu.org\n*.hkwcc.org.hk\n*.hongkongfp.com\n*.hongkongtibetfilmfestival2015.com\n*.hotchyx.com\n*.hotspotshield.com\n*.hrichina.org\n*.hrw.org\n*.huanghuagang.org\n*.huaren.us\n*.huaxia-news.com\n*.huping.net\n*.hutong9.net\n*.hwinfo.com\n*.hxmmdd.com\n*.hyperrate.com\n*.hzy.pw\n*.i1.hk\n*.i2ocr.com\n*.i2p2.de\n*.iask.ca\n*.iceimg.com\n*.icij.org\n*.idol-mile.com\n*.idv.tw\n*.ifanqiang.com\n*.ift.tt\n*.igfw.???\n*.igfw.tk\n*.igossip.com\n*.ihao.org\n*.ihd1080.org\n*.ihktv.com\n*.imagebam.com\n*.imageshack.us\n*.imagestorming.com\n*.imageurlhost.com\n*.imagevenue.com\n*.imagezilla.net\n*.img.ly\n*.imgburn.com\n*.imgchili.com\n*.imgchili.net\n*.imgdino.com\n*.imgkeep.com\n*.imgly.net\n*.imgtiger.com\n*.imgur.com\n*.immoral.jp\n*.i-mobile.co.jp\n*.inmediahk.net\n*.inote.tw\n*.inside.com.tw\n*.instagram.com\n*.insynchq.com\n*.intermargins.net\n*.internet.org\n*.internetfreedom.org\n*.inxian.com\n*.ipcf.org.tw\n*.ipicture.ru\n*.ipkmedia.com\n*.isohunt.com\n*.isunaffairs.com\n*.isuntv.com\n*.ithelp.ithome.com.tw\n*.ixxx.com\n*.iyouport.com\n*.iyouport.org\n*.j.mp\n*.jasonsavard.com\n*.jav008.com\n*.javblog.biz\n*.javfree.me\n*.javideo.info\n*.javsharing.com\n*.jayxon.com\n*.jbtalks.cc\n*.jinbushe.org\n*.jingfeng.info\n*.jingpin.org\n*.jiruan.net\n*.jjgirls.com\n*.jkforum.net\n*.jpavgod.com\n*.justin.tv\n*.just-ping.com\n*.kan.center\n*.kankan.today\n*.keakon.net\n*.keephkshining.com\n*.kenengba.com\n*.kexueshangwang.info\n*.kinghost.com\n*.kingstone.com.tw\n*.kir.jp\n*.kwongwah.com.my\n*.la-forum.org\n*.lagranepoca.com\n*.lailaibt.com\n*.laqingdan.net\n*.latteye.com\n*.lcx.cc\n*.lefora.com\n*.left21.hk\n*.lemonde.fr\n*.lesoir.be\n*.letscorp.net\n*.libertytimes.com.tw\n*.libreoffice.org\n*.life.com.tw\n*.limbopro.xyz\n*.lineageosrom.com\n*.line-scdn.net\n*.linkbucks.com\n*.listhub.net\n*.liuxiaobo.net\n*.liveleak.com\n*.livestation.com\n*.livestream.com\n*.localpresshk.com\n*.lockerz.com\n*.lolbin.net\n*.loli.net\n*.longhair.hk\n*.lookpic.com\n*.loved.hk\n*.lrip.org\n*.lsd.org.hk\n*.lsj1080.cc\n*.lsj1080.net\n*.lsj1080.tv\n*.ltn.com.tw\n*.lvv2.com\n*.mail-archive.com\n*.maiplus.com\n*.malaymail.com\n*.malaysiakini.com\n*.matters.news\n*.mattwilcox.net\n*.medium.com\n*.mefeedia.com\n*.memehk.com\n*.merit-times.com\n*.merit-times.com.tw\n*.merlinblog.xyz\n*.metrolife.ca\n*.metroradio.com.hk\n*.microad.jp\n*.minghui.org\n*.mingjinglishi.com\n*.mingjingnews.com\n*.mingpaonews.com\n*.mingshengbao.com\n*.minnano-av.com\n*.minus.com\n*.minzhuzhongguo.org\n*.mirrorbooks.com\n*.mitbbs.com\n*.mkini.net\n*.mlxiaoshuo.com\n*.mobile01.com\n*.mobileways.de\n*.mobypicture.com\n*.moedict.tw\n*.mojim.com\n*.mokeedev.com\n*.molihua.org\n*.mono.ac\n*.mono.sh\n*.moonbbs.com\n*.mozilla.net\n*.mp3ye.eu\n*.mpfinance.com\n*.msguancha.com\n*.mtlmp4.com\n*.myca168.com\n*.mychinanews.com\n*.mycnnews.com\n*.mycould.com\n*.myfreecams.com\n*.myfreshnet.com\n*.myhd1080.tv\n*.myradio.com.hk\n*.myradio.hk\n*.mysinablog.com\n*.nanyang.com\n*.nanyangpost.com\n*.ncchinesenews.com\n*.ndr.de\n*.net1.hkbu.edu.hk\n*.netgear.com\n*.netme.cc\n*.network54.com\n*.networkedblogs.com\n*.newcenturymc.com\n*.newcenturynews.com\n*.newhighlandvision.com\n*.news.hk.msn.com\n*.news.pts.org.tw\n*.newsancai.com\n*.newstapa.org\n*.newtaiwan.com.tw\n*.newtalk.tw\n*.nextdigital.com.hk\n*.nextmag.com.tw\n*.nextmedia.com\n*.ngensis.com\n*.nicovideo.jp\n*.nobel.se\n*.nobelprize.org\n*.notepad-plus-plus.org\n*.notipage.com\n*.nownews.com\n*.nps.gov\n*.nrk.no\n*.ntd.tv\n*.ntdtv.com\n*.ntdtv-dc.com\n*.nuzcom.com\n*.nvquan.org\n*.nyt.com\n*.nytcn.me\n*.nyti.ms\n*.nytimes.com\n*.nytimg.com\n*.nytstyle.com\n*.observechina.net\n*.oclp.hk\n*.ogaoga.org\n*.oiktv.com\n*.olx.com.br\n*.on.cc\n*.onedrive.live.com\n*.onlinestuffs.com\n*.open.com.hk\n*.opendemocracy.net\n*.openstreetmap.org\n*.orientaldaily.com.my\n*.orientaldaily.on.cc\n*.orzhd.com\n*.oursogo.com\n*.oursteps.com.au\n*.outbrain.com\n*.ow.ly\n*.oyax.com\n*.oyou.com.au\n*.page2rss.com\n*.pageflakes.com\n*.panoramio.com\n*.pao-pao.net\n*.paper.li\n*.passiontimes.hk\n*.pbwiki.com\n*.pbworks.com\n*.pcdvd.com.tw\n*.pchome.com.tw\n*.pcij.org\n*.peacehall.com\n*.penchinese.org\n*.peopo.org\n*.perfspot.com\n*.pfd.org.hk\n*.picpar.com\n*.picrar.com\n*.piebridge.me\n*.pimgs.com\n*.pincong.rocks\n*.ping.fm\n*.pinimg.com\n*.pinterest.com\n*.piratescreen.com\n*.piring.com\n*.pixshock.net\n*.playno1.com\n*.playno1.com.tw\n*.popvote.hk\n*.popyard.com\n*.popyard.org\n*.porn.com\n*.pornhub.com\n*.post852.com\n*.posterous.com\n*.potatso.com\n*.potatsocontent.com\n*.powerlinks.com\n*.premeforwindows.com\n*.prestige-av.com\n*.prettyvirgin.com\n*.privoxy.org\n*.pubu.com.tw\n*.qidian.ca\n*.qiwen.lu\n*.quickpornsearch.com\n*.quora.com\n*.quoracdn.net\n*.qxbbs.org\n*.radioaustralia.net.au\n*.ranyunfei.com\n*.rapbull.net\n*.rarbg.to\n*.rcinet.ca\n*.readingtimes.com.tw\n*.rebecca-web.com\n*.recovery.org.tw\n*.redchinacn.org\n*.reddit.com\n*.redsquirrel87.com\n*.redtube.com\n*.reduik.com\n*.referer.us\n*.relink.us\n*.rendsmap.com\n*.renminbao.com\n*.resilio.com\n*.restorehk.com\n*.reuters.com\n*.reutersmedia.net\n*.rfa.org\n*.rferl.org\n*.rfi.fr\n*.rfi.my\n*.rghost.net\n*.riku.me\n*.rmjdw.com\n*.rnw.nl\n*.rocmp.org\n*.roodo.com\n*.rsf.org\n*.rsf-chinese.org\n*.rthk.hk\n*.rthk.org.hk\n*.rti.org.tw\n*.s3.amazonaws.com\n*.sadpanda.us\n*.sanmin.com.tw\n*.sanminjiaoliu.net\n*.savemedia.com\n*.savetube.com\n*.savevid.com\n*.saveyoutube.com\n*.scdn.co\n*.scholarism.com\n*.s-dragon.org\n*.search.xxx\n*.secretchina.com\n*.securityinabox.org\n*.securitykiss.com\n*.sendspace.com\n*.setn.com\n*.sex.com\n*.sexinsex.net\n*.shadow.ma\n*.shadowgov.tw\n*.shadowsocks.org\n*.sharenxs.com\n*.sharpdaily.com\n*.sharpdaily.tw\n*.sherrychan.net\n*.shizhao.org\n*.shr.lc\n*.shutterstock.com\n*.sinaapp.co\n*.singtao.com\n*.sinomontreal.ca\n*.sinoquebec.com\n*.sis001.com\n*.sitebro.tw\n*.six-degrees.io\n*.slideshare.net\n*.smh.com.au\n*.smhric.org\n*.soduso.com\n*.softether-download.com\n*.sohcradio.com\n*.sondelespoir.org\n*.sonidodelaesperanza.org\n*.sougouwiki.com\n*.soundofhope.co.kr\n*.soundofhope.kr\n*.soundofhope.org\n*.soup-dev.com\n*.sourceforge.net\n*.southnews.com.tw\n*.spankwire.com\n*.spotify.com\n*.spring4u.info\n*.ssr.tools\n*.startpage.com\n*.static-file.com\n*.steemit.com\n*.stgloballink.com\n*.stickeraction.com\n*.stimme-de.de\n*.stooorage.com\n*.storify.com\n*.storm.mg\n*.stormmediagroup.com\n*.strongvpn.com\n*.stumbleupon.com\n*.stupidvideos.com\n*.sucuri.net\n*.sugarsync.com\n*.sun1911.com\n*.supersu.com\n*.swagbucks.com\n*.sydneytoday.com\n*.t.co\n*.t.me\n*.t66y.com\n*.taaze.tw\n*.tahr.org.tw\n*.taiwandaily.net\n*.taiwanus.net\n*.talkonly.net\n*.tampermonkey.net\n*.tantannews.com\n*.tavis.tw\n*.tca.org.tw\n*.tdesktop.com\n*.techinasia.com\n*.technorati.com\n*.telegra.ph\n*.telegram.me\n*.telegram.org\n*.telesco.pe\n*.tenacy.com\n*.tenacy-free.com\n*.theblaze.com\n*.thebobs.com\n*.theepochtimes.com\n*.thefrontier.hk\n*.theglobalmail.org\n*.theguardian.com\n*.thehousenews.com\n*.theinitium.com\n*.thenewslens.com\n*.the-sun.on.cc\n*.thetibetpost.com\n*.thetimenow.com\n*.thinkingtaiwan.com\n*.thisav.com\n*.tiananmenduizhi.com\n*.tiananmenmother.org\n*.tiananmenuniv.com\n*.tiananmenuniv.net\n*.tibet.net\n*.time.com\n*.timer-tab.com\n*.tiny.cc\n*.tinychat.com\n*.tmagazine.com\n*.tmu.edu.tw\n*.tokyocn.com\n*.topcoo.com\n*.topsy.com\n*.torrentcrazy.com\n*.torrentkitty.com\n*.trackon.org\n*.transformativeworks.org\n*.transparency.org\n*.trendsmap.com\n*.trib.al\n*.trouw.nl\n*.trtc.com.tw\n*.trueimages.ru\n*.truveo.com\n*.tsquare.tv\n*.tsu.org.tw\n*.tube8.com\n*.tuiyun.net\n*.tumblr.com\n*.tv.com\n*.tvboxnow.com\n*.twbbs.org\n*.twbbs.tw\n*.tweetdeck.com\n*.tweettunnel.com\n*.tweez.net\n*.twgreatdaily.com\n*.twicsy.com\n*.twilk.com\n*.twimg.com\n*.twipple.jp\n*.twitchtv.com\n*.twitiq.com\n*.twitpic.com\n*.twitter.com\n*.twitterfeed.com\n*.twittergadget.com\n*.twitterknowhow.com\n*.twitvid.com\n*.twitvid.com.edgesuite.net\n*.twiyia.com\n*.twtrland.com\n*.ucdc1998.org\n*.udn.com\n*.udn.com.tw\n*.ugo.com\n*.uhrp.org\n*.uighurbiz.net\n*.ulifestyle.com.hk\n*.ultrareach.com\n*.unblock.cn.com\n*.uncyclopedia.tw\n*.upmedia.mg\n*.uproxy.org\n*.ustream.tv\n*.utorrent.com\n*.uwants.com\n*.uyghuramerican.org\n*.uyghurpress.com\n*.uyl.me\n*.v2ex.com\n*.van698.com\n*.vaticanradio.org\n*.vdonext.com\n*.velkaepocha.sk\n*.venchina.com\n*.veoh.com\n*.vevo.com\n*.viber.com\n*.video.pbs.org\n*.vietdaikynguyen.com\n*.vinmusic.com\n*.visiontimes.ca\n*.vjmedia.com.hk\n*.vocus.cc\n*.vot.org\n*.vox-cdn.com\n*.vpngate.jp\n*.vpngate.net\n*.vpnxunlu.com\n*.wahas.com\n*.wanglixiong.com\n*.want-daily.com\n*.watchzerg.com\n*.watorrent.org\n*.wattpad.com\n*.wdf5.com\n*.wearn.com\n*.web.pts.org.tw\n*.weblagu.com\n*.websitepulse.com\n*.webwarper.net\n*.wegfw.com\n*.weibosuite.com\n*.weijingsheng.org\n*.weiquanwang.org\n*.wengewang.org\n*.wenxuecity.com\n*.westca.com\n*.westkit.net\n*.wetransfer.com\n*.whattalking.com\n*.whogovernstw.org\n*.whotalking.com\n*.wi-gadget.com\n*.wikia.com\n*.wikia.net\n*.wikia.nocookie.net\n*.wikia-beacon.com\n*.wikileaks.org\n*.wikilivres.ca\n*.wiztechnologies.jp\n*.wn.com\n*.woolyss.com\n*.worldcat.org\n*.worldjournal.com\n*.wp.com\n*.wp.me\n*.wrchina.org\n*.wretch.cc\n*.wuala.com\n*.wuerkaixi.com\n*.wujie.net\n*.wujieliulan.com\n*.wwitv.com\n*.xanga.com\n*.xbookcn.net\n*.xhamster.com\n*.xiaochuncnjp.com\n*.xinmiao.com.hk\n*.xinyubbs.net\n*.xiti.com\n*.xizang-zhiye.org\n*.xjp.cc\n*.xn--fiq681d48s.org\n*.xpics.us\n*.xtube.com\n*.xuehua.us\n*.xunluvpn.com\n*.xvideos.com\n*.xvideos-field5.com\n*.xvideosmatome.net\n*.xys.org\n*.yamedia.tw\n*.yasni.co.uk\n*.ycp.hk\n*.yfrog.com\n*.yhritw.org\n*.yibaochina.com\n*.yidio.com\n*.yorkbbs.ca\n*.you2go.me\n*.youjizz.com\n*.youmaker.com\n*.youporn.com\n*.youthwant.com.tw\n*.youtu.be\n*.youtube.com\n*.youtubeinmp3.com\n*.yowindow.com\n*.ytimg.com\n*.yzzk.com\n*.zhengjian.org\n*.zhenlibu1984.com\n*.zhongzilou.com\n*.zhoushuguang.com\n*.zinio.com\n*.zlvc.net\n*.zlvc.net.he2.aqb.so\n*.zomobo.net\n*.zorpia.com\n*.zuo.la\n*.zuobiao.me\n*.zuola.com\n*.zxing.org\n*.zyzc.greatzhonghua.org\n.cdninstagram.com\n.myhd1080.com\n\\bhttp://(.[^/])*\\.*\\bdelicious\\.com/\n^http://search\\.aol\\.com/\n184.154.128.246\n75.119.209.87\nad.dmm.com\naddons-amo.cdn.mozilla.net\nbbs.51.ca\nbufferapp.com\nc.statcounter.com\ncdn.media.abc.com\ncdn.rawgit.com\ncdn.viafoura.net\ncdns.gigya.com\nchinese.engadget.com\ncommunity.emc.com\ndl.xda-developers.com\ndownload.documentfoundation.org\ndownload.lineageos.org\ndownload.tuxfamily.org\ndownloadap1.teamviewer.com\ndownload-cdn.resilio.com\nforum.gamer.com.tw\nforum.games.hinet.net\nfpdownload.macromedia.com\nftp.mozilla.org\nhcd-1.imgbox.com\nhk03dl.com\nHostRegex: ^((.+\\.c\\.doc-0-0-sj\\.|)sj|ci\\d{1,2}|oauth|webcache|\\d-ps|uds|producer|lh\\d|s\\d|.+-opensocial|clients\\d|translate|mail-attachment|.+-docs)\\.googleusercontent\\.com$\nHostRegex: ^((id|scholar|news|accounts|adwords|books|images|khms\\d|www|blogsearch|cse|encrypted|m|translate)\\.|)google\\.(^|com\\.af|com\\.ag|com\\.ai|co\\.ao|com\\.ar|com\\.au|com\\.bd|com\\.bh|com\\.bn|com\\.bo|com\\.br|co\\.bw|com\\.bz|co\\.ck|com\\.co|co\\.cr|com\\.cu|com\\.cy|com\\.do|com\\.ec|com\\.eg|com\\.et|com\\.fj|com\\.gh|com\\.gi|com\\.gt|com\\.hk|co\\.id|co\\.il|co\\.in|com\\.jm|co\\.jp|co\\.ke|com\\.kh|co\\.kr|com\\.kw|com\\.lb|co\\.ls|com\\.ly|co\\.ma|com\\.mm|com\\.mt|com\\.mx|com\\.my|co\\.mz|com\\.na|com\\.nf|com\\.ng|com\\.ni|com\\.np|co\\.nz|com\\.om|com\\.pa|com\\.pe|com\\.pg|com\\.ph|com\\.pk|com\\.pr|com\\.py|com\\.qa|com\\.sa|com\\.sb|com\\.sg|com\\.sl|com\\.sv|co\\.th|com\\.tj|com\\.tr|com\\.tw|co\\.tz|com\\.ua|co\\.ug|co\\.uk|com\\.uy|co\\.uz|com\\.vc|co\\.ve|co\\.vi|com\\.vn|co\\.za|co\\.zm|co\\.zw|cat|ad|ae|al|am|as|at|az|ba|be|bf|bg|bi|bj|bs|bt|by|ca|cd|cf|cg|ch|ci|cl|cm|com|cv|cz|de|dj|dk|dm|dz|ee|es|fi|fm|fr|ga|ge|gg|gl|gm|gp|gr|gy|hn|hr|ht|hu|ie|im|iq|is|it|je|jo|kg|ki|kz|la|li|lk|lt|lu|lv|md|me|mg|mk|ml|mn|ms|mu|mv|mw|ne|nl|no|nr|nu|pl|pn|ps|pt|ro|rs|ru|rw|sc|se|sh|si|sk|sm|sn|so|sr|st|td|tg|tk|tl|tm|tn|to|tt|vg|vu|ws|jp|tw|hk)$\nHostRegex: ^((uploads\\.|)code(|\\.l)|.*\\bdocs|doc|.*\\bdrive|play|plus|plus\\.url|buzz|profiles|mail|apis|support|wallet|checkout|talkgadget|appengine|store|security|myaccount|chrome|contacts|safebrowsing(|-cache|\\.clients)|calendar|clients\\d|mt(|s)\\d|picasa\\w*|sites|fusion|reader|feed\\w*|input|research|inbox|mw\\d|gg|peering|events|fit|tools|music|on|photo(|s))\\.google\\.com$\nHostRegex: ^(daily|)news\\.sina\\.com(\\.(hk|tw)|)$\nHostRegex: ^(feedproxy|feedburner|hangouts|keep)\\.google\\.com$\nHostRegex: ^(maps|www|translate|plus|ajax|mts\\d|commondatastorage|chart|storage|fonts)\\.googleapis\\.com$\nHostRegex: ^(skins|www(|\\.ig))\\.gmodules\\.com$\nHostRegex: ^(space|bbs)\\.qoos\\.com$\nHostRegex: ^.*\\b(blogger|blogspot)\\.(^|com\\.af|com\\.ag|com\\.ai|co\\.ao|com\\.ar|com\\.au|com\\.bd|com\\.bh|com\\.bn|com\\.bo|com\\.br|co\\.bw|com\\.bz|co\\.ck|com\\.co|co\\.cr|com\\.cu|com\\.cy|com\\.do|com\\.ec|com\\.eg|com\\.et|com\\.fj|com\\.gh|com\\.gi|com\\.gt|com\\.hk|co\\.id|co\\.il|co\\.in|com\\.jm|co\\.jp|co\\.ke|com\\.kh|co\\.kr|com\\.kw|com\\.lb|co\\.ls|com\\.ly|co\\.ma|com\\.mm|com\\.mt|com\\.mx|com\\.my|co\\.mz|com\\.na|com\\.nf|com\\.ng|com\\.ni|com\\.np|co\\.nz|com\\.om|com\\.pa|com\\.pe|com\\.pg|com\\.ph|com\\.pk|com\\.pr|com\\.py|com\\.qa|com\\.sa|com\\.sb|com\\.sg|com\\.sl|com\\.sv|co\\.th|com\\.tj|com\\.tr|com\\.tw|co\\.tz|com\\.ua|co\\.ug|co\\.uk|com\\.uy|co\\.uz|com\\.vc|co\\.ve|co\\.vi|com\\.vn|co\\.za|co\\.zm|co\\.zw|cat|ad|ae|al|am|as|at|az|ba|be|bf|bg|bi|bj|bs|bt|by|ca|cd|cf|cg|ch|ci|cl|cm|com|cv|cz|de|dj|dk|dm|dz|ee|es|fi|fm|fr|ga|ge|gg|gl|gm|gp|gr|gy|hn|hr|ht|hu|ie|im|iq|is|it|je|jo|kg|ki|kz|la|li|lk|lt|lu|lv|md|me|mg|mk|ml|mn|ms|mu|mv|mw|ne|nl|no|nr|nu|pl|pn|ps|pt|ro|rs|ru|rw|sc|se|sh|si|sk|sm|sn|so|sr|st|td|tg|tk|tl|tm|tn|to|tt|vg|vu|ws|jp|tw|hk|sg)$\nHostRegex: ^.*\\b(scmp(chinese|)|nanzao)\\.com$\nHostRegex: ^.*\\balanleong\\.(net|com)$\nHostRegex: ^.*\\bappledaily\\.com(|\\.(tw|hk))$\nHostRegex: ^.*\\bavlang\\d+\\.com$\nHostRegex: ^.*\\bbbc(i|)\\.co\\.uk$\nHostRegex: ^.*\\bbbc\\.(com|in)$\nHostRegex: ^.*\\bbeacons(|\\d)\\.gvt2\\.com$\nHostRegex: ^.*\\bbloomberg\\.(com|(i|c)n)$\nHostRegex: ^.*\\bcdp(|2006|wu|site|1998)\\.org$\nHostRegex: ^.*\\bdwnews\\.(com|net(:\\d+|))$\nHostRegex: ^.*\\bepoch(times(-romania|tr|-bg)|tw|hk)\\.com$\nHostRegex: ^.*\\bepochtimes\\.(com\\.(tw|hk|ua)|com|fr|de|co\\.(il|kr)|jp|ru|it|se)$\nHostRegex: ^.*\\bg(nci|cc)\\.org\\.hk$\nHostRegex: ^.*\\bgardennetworks\\.(org|com)$\nHostRegex: ^.*\\bgreatroc\\.(tw|org)$\nHostRegex: ^.*\\bkagoya\\.(net|jp)$\nHostRegex: ^.*\\bkanzhongguo\\.(com(\\.au|)|eu)$\nHostRegex: ^.*\\bmingpao(|ny|tor|van|canada|news|monthly)\\.com$\nHostRegex: ^.*\\bnext(tv|vod)\\.com\\.tw$\nHostRegex: ^.*\\bntdtv(|-dc)\\.com$\nHostRegex: ^.*\\bthepiratebay\\.(s(e|x)|org)$\nHostRegex: ^.*\\bthywords\\.com(|\\.tw)$\nHostRegex: ^.*\\bvoa(fanti|chinese|cantonese|tibetan(|english))\\.com$\nHostRegex: ^.*\\bvoanews\\.(com|eu)$\nHostRegex: ^.*\\bwsj\\.(com|net)$\nHostRegex: ^.*\\byoutube\\.(^|com\\.af|com\\.ag|com\\.ai|co\\.ao|com\\.ar|com\\.au|com\\.bd|com\\.bh|com\\.bn|com\\.bo|com\\.br|co\\.bw|com\\.bz|co\\.ck|com\\.co|co\\.cr|com\\.cu|com\\.cy|com\\.do|com\\.ec|com\\.eg|com\\.et|com\\.fj|com\\.gh|com\\.gi|com\\.gt|com\\.hk|co\\.id|co\\.il|co\\.in|com\\.jm|co\\.jp|co\\.ke|com\\.kh|co\\.kr|com\\.kw|com\\.lb|co\\.ls|com\\.ly|co\\.ma|com\\.mm|com\\.mt|com\\.mx|com\\.my|co\\.mz|com\\.na|com\\.nf|com\\.ng|com\\.ni|com\\.np|co\\.nz|com\\.om|com\\.pa|com\\.pe|com\\.pg|com\\.ph|com\\.pk|com\\.pr|com\\.py|com\\.qa|com\\.sa|com\\.sb|com\\.sg|com\\.sl|com\\.sv|co\\.th|com\\.tj|com\\.tr|com\\.tw|co\\.tz|com\\.ua|co\\.ug|co\\.uk|com\\.uy|co\\.uz|com\\.vc|co\\.ve|co\\.vi|com\\.vn|co\\.za|co\\.zm|co\\.zw|cat|ad|ae|al|am|as|at|az|ba|be|bf|bg|bi|bj|bs|bt|by|ca|cd|cf|cg|ch|ci|cl|cm|cn|com|cv|cz|de|dj|dk|dm|dz|ee|es|fi|fm|fr|ga|ge|gg|gl|gm|gp|gr|gy|hn|hr|ht|hu|ie|im|iq|is|it|je|jo|kg|ki|kz|la|li|lk|lt|lu|lv|md|me|mg|mk|ml|mn|ms|mu|mv|mw|ne|nl|no|nr|nu|pl|pn|ps|pt|ro|rs|ru|rw|sc|se|sh|si|sk|sm|sn|so|sr|st|td|tg|tk|tl|tm|tn|to|tt|vg|vu|ws|jp|tw|hk)$\nHostRegex: ^38\\.103\\.161\\.\\d{1,3}$\nimages.secdns.com\ninternational.sueddeutsche.de\nirs0.4sqi.net\nlibs.pixfs.net\nmirrorbits.lineageos.org\npds.nasa.gov\npics.dmm.co.jp\nplaza.jp.rakuten-static.com\ns1.pir.fm\nsendtokindle.amazon.cn\nsupport.mozilla.org\ntimes.hinet.net\ntorrent.ubuntu.com\ntweets.seraph.me\ntwimg.edgesuite.net\nUrlRegex: ^http(|s)://(.[^/])*\\.*\\bak\\.live\\.cntv\\.cn/z/\nUrlRegex: ^http(|s)://\\d+\\.client-channel\\.google\\.com/client-channel/js/\nUrlRegex: ^http(|s)://windows\\.microsoft\\.com/.*/windows/themes\nUrlRegex: ^http://(.[^/])*\\.*\\b(home|forum)\\.gamer\\.com\\.tw\nUrlRegex: ^http://(.[^/])*\\.*\\b(news|blog)\\.cnyes\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\b(news|popblog)\\.tvbs\\.com\\.tw/\nUrlRegex: ^http://(.[^/])*\\.*\\b(showbiz|forum|news|cling)\\.omy\\.sg/\nUrlRegex: ^http://(.[^/])*\\.*\\b(trans|blog)wenweipo\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\b2o7\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\b881903\\.com/Page/ZH-TW/index\nUrlRegex: ^http://(.[^/])*\\.*\\ba164\\.edgecastcdn\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\badsbro\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\baolnews\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bapi\\.i\\.pixnet\\.cc/\nUrlRegex: ^http://(.[^/])*\\.*\\barchive\\.is/\nUrlRegex: ^http://(.[^/])*\\.*\\barchive\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bavast\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bbitly\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bbitsnoop\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bblog\\.livedoor\\.jp/\nUrlRegex: ^http://(.[^/])*\\.*\\bblogsys\\.jp/\nUrlRegex: ^http://(.[^/])*\\.*\\bbluekai.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bbtdigg.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bbuzzhand\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bcable(v|news)\\.i-cable\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bchange\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bchapm25\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bchartbeat\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\bcloudflare\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bcloudfront\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\bclustrmaps.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bcrwdcntrl\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\bcxense.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bdisqus\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bdowjoneson.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bdropbox\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bdropboxusercontent\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bduckduckgo\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bdxtl\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\bdynamicyield\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bensighten\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\beslite\\.com/product\nUrlRegex: ^http://(.[^/])*\\.*\\bfeedly\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bflip\\.it/\nUrlRegex: ^http://(.[^/])*\\.*\\bfout.jp/\nUrlRegex: ^http://(.[^/])*\\.*\\bgetemoji\\.com/cdn-cgi/pe/\nUrlRegex: ^http://(.[^/])*\\.*\\bgetpocket\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bgooglesyndication\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bhaxx\\.se/\nUrlRegex: ^http://(.[^/])*\\.*\\bhkxforce\\.net/wordpress/\nUrlRegex: ^http://(.[^/])*\\.*\\bhootsuite\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bimage\\.blozoo\\.info/\nUrlRegex: ^http://(.[^/])*\\.*\\bimgur\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bimrworldwide.com/\nUrlRegex: ^http://(.[^/])*\\.*\\binstagram\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bkakao\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bkrxd\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\bksnews\\.com\\.tw/\nUrlRegex: ^http://(.[^/])*\\.*\\blijit\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bline\\.me/\nUrlRegex: ^http://(.[^/])*\\.*\\blongurl\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bmassrelevance\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bmediafire\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bmediawiki\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bmetrohk\\.com\\.hk/\\?cmd=detail&categoryID=2\nUrlRegex: ^http://(.[^/])*\\.*\\bmoby\\.to/\nUrlRegex: ^http://(.[^/])*\\.*\\bmoneydj\\.com/(KMDJ|kmdj)\nUrlRegex: ^http://(.[^/])*\\.*\\bmyhd1080\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bnetsh\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bnews\\.singtao\\.ca/\nUrlRegex: ^http://(.[^/])*\\.*\\bnews\\.tvb\\.com/(local|list/world)\nUrlRegex: ^http://(.[^/])*\\.*\\bnyaa\\.se/\nUrlRegex: ^http://(.[^/])*\\.*\\bomtrdc\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\boo-software\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bopendns\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bopenx\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\boutbrain\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bparsely\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bpastebin\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bpastie\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bpixnet\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\bpo\\.st/\nUrlRegex: ^http://(.[^/])*\\.*\\bprisacom\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bpsiphon\\.ca/\nUrlRegex: ^http://(.[^/])*\\.*\\bpsiphon3\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bptt\\.cc/\nUrlRegex: ^http://(.[^/])*\\.*\\bpuffstore\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bquantserve\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\breferer\\.pixplug\\.in/\nUrlRegex: ^http://(.[^/])*\\.*\\brevsci\\.net/\nUrlRegex: ^http://(.[^/])*\\.*\\bscribd\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bsharethis\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bsinchew(|-i)\\.com(|\\.my)/node\nUrlRegex: ^http://(.[^/])*\\.*\\bsitemeter\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bsmarturl\\.it/\nUrlRegex: ^http://(.[^/])*\\.*\\bstarboard-pro\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bstatic-file\\.com/shared/upload/video/.*0604\nUrlRegex: ^http://(.[^/])*\\.*\\btalkboxapp.com/\nUrlRegex: ^http://(.[^/])*\\.*\\btenmax\\.io/\nUrlRegex: ^http://(.[^/])*\\.*\\bthestandnews\\.com\nUrlRegex: ^http://(.[^/])*\\.*\\btokyo-hot\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\btorproject\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\btorrentproject.se/\nUrlRegex: ^http://(.[^/])*\\.*\\btorrentz\\.eu/\nUrlRegex: ^http://(.[^/])*\\.*\\btraffic\\.alexa\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\btumblr\\.com\nUrlRegex: ^http://(.[^/])*\\.*\\btumutanzi\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\btynt.com/\nUrlRegex: ^http://(.[^/])*\\.*\\budnbkk\\.com/(article|bbs)\nUrlRegex: ^http://(.[^/])*\\.*\\bus\\d+\\.gigya\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bvideo\\.ap\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bvimeo\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bviss\\.me/\nUrlRegex: ^http://(.[^/])*\\.*\\bvisualrevenue\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bwebtrendslive\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bwhoer\\.net/check\\?host=\nUrlRegex: ^http://(.[^/])*\\.*\\bwiki(source|quote|books|news|voyage|versity|data|media|pedia)\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bwikimediafoundation\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bwiktionary\\.org/\nUrlRegex: ^http://(.[^/])*\\.*\\bwoopra\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bwordpress\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\bwzyboy\\.im/\nUrlRegex: ^http://(.[^/])*\\.*\\bxing\\.com/\nUrlRegex: ^http://(.[^/])*\\.*\\byahoo\\.(com|com\\.(hk|tw)|co\\.jp)/\nUrlRegex: ^http://(.[^/])*\\.*\\byandex\\.ru/\nUrlRegex: ^http://(.[^/])*\\.*\\byoutu\\.be/\nUrlRegex: ^http://(.[^/])*\\.*\\bzedo\\.com/asw/\nUrlRegex: ^http://(library|mjlsh)\\.usc\\.cuhk\\.edu\\.hk/\nUrlRegex: ^http://66\\.147\\.244\\.112/~hchinaor/\nUrlRegex: ^http://67\\.220\\.92\\.\\d+/\nUrlRegex: ^http://ad\\.tagtoo\\.co/\nUrlRegex: ^http://adadvisor\\.net/\nUrlRegex: ^http://ads\\.contextweb\\.com/TagPublish/\nUrlRegex: ^http://adsense\\.scupio\\.com/ADPInline/\nUrlRegex: ^http://api\\.microsofttranslator\\.com/\nUrlRegex: ^http://api-public\\.addthis\\.com/url/\nUrlRegex: ^http://b\\.scorecardresearch\\.com/\nUrlRegex: ^http://bbs\\.cantonese\\.asia/\nUrlRegex: ^http://bdaz\\.adsfactor\\.net/\nUrlRegex: ^http://bgp\\.he\\.net/\nUrlRegex: ^http://bs\\.serving-sys\\.com/BurstingPipe/\nUrlRegex: ^http://ca\\.pcrookie\\.net/blog_images/\nUrlRegex: ^http://cdn\\.unwire\\.hk/wp-content/uploads/.*(WordPress|www\\.youtube\\.com)\nUrlRegex: ^http://chinese\\.irib\\.ir/index\\.php/2010-07-25-10-42-26/\nUrlRegex: ^http://citytalk\\.tw/event\nUrlRegex: ^http://cn\\.last\\.fm/facebook/\nUrlRegex: ^http://cpms\\.now\\.com/cpms/\nUrlRegex: ^http://data\\.inskinmedia\\.com/trackports/rep/base/\nUrlRegex: ^http://dmp\\.doublemax\\.net/\nUrlRegex: ^http://edigitalsurvey\\.com/\nUrlRegex: ^http://e-zone\\.com\\.hk/discuz\nUrlRegex: ^http://fileserve\\.com/file\nUrlRegex: ^http://findbook\\.tw/book\nUrlRegex: ^http://forum\\.xinbao\\.de/forum\nUrlRegex: ^http://hkupop\\.hku\\.hk/chinese/\nUrlRegex: ^http://humanities\\.uchicago\\.edu/faculty/ywang/history\nUrlRegex: ^http://img\\.readitlater\\.com/i/\nUrlRegex: ^http://news\\.now\\.com/home\nUrlRegex: ^http://news\\.tagtoo\\.co/site_media/plugin/nownews_tagtoo_plugin\\.js\nUrlRegex: ^http://p\\.typekit\\.net/\nUrlRegex: ^http://r\\.skimresources\\.com/api/\nUrlRegex: ^http://rapidgator\\.net/file/\nUrlRegex: ^http://seehua\\.com/node/\nUrlRegex: ^http://servedby\\.flashtalking\\.com/\nUrlRegex: ^http://tas-hk\\.toboads\\.com/js/\nUrlRegex: ^http://track\\.tagtoo\\.co/\nUrlRegex: ^http://u\\.scupio\\.com/\nUrlRegex: ^http://v?\\.moatads\\.com/\nUrlRegex: ^http://wiki\\.moegirl\\.org/\nUrlRegex: ^http://www\\.blog(cdn|smithmedia)\\.com/chinese\\.engadget\\.com/\nUrlRegex: ^http://www\\.chinasmile\\.net/csnews/\nUrlRegex: ^http://www\\.greenpeace\\.org/\nUrlRegex: ^http://www\\.kwcg\\.ca/forum\nUrlRegex: ^http://www\\.linkedin\\.com/countserv/count/\nUrlRegex: ^http://www\\.manictime\\.com/setup/\nUrlRegex: ^http://www\\.marxists\\.org/chinese\nUrlRegex: ^https://(.[^/])*\\.*\\baddthis\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\barchive\\.is/\nUrlRegex: ^https://(.[^/])*\\.*\\barchive\\.org/\nUrlRegex: ^https://(.[^/])*\\.*\\bbtdigg\\.org/\nUrlRegex: ^https://(.[^/])*\\.*\\bchange\\.org/\nUrlRegex: ^https://(.[^/])*\\.*\\bcloudflare\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bcloudfront\\.net/\nUrlRegex: ^https://(.[^/])*\\.*\\bdropbox\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bduckduckgo\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bfeedly\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bgetpocket\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bhootsuite\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\binstagram.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bmassrelevance\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bmediawiki\\.org/\nUrlRegex: ^https://(.[^/])*\\.*\\bonebitbug\\.me/\nUrlRegex: ^https://(.[^/])*\\.*\\bopendns\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bpastebin\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bpsiphon\\.ca/\nUrlRegex: ^https://(.[^/])*\\.*\\bpsiphon3\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bptt\\.cc/\nUrlRegex: ^https://(.[^/])*\\.*\\bscribd\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bsharethis\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bsitemeter\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bthestandnews\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\btorproject\\.org/\nUrlRegex: ^https://(.[^/])*\\.*\\btorrentz\\.eu/\nUrlRegex: ^https://(.[^/])*\\.*\\bvimeo\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bwiki(source|quote|books|news|voyage|versity|data|media|pedia)\\.org/\nUrlRegex: ^https://(.[^/])*\\.*\\bwikimediafoundation\\.org/\nUrlRegex: ^https://(.[^/])*\\.*\\bwiktionary\\.org/\nUrlRegex: ^https://(.[^/])*\\.*\\bwordpress\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\bxing\\.com/\nUrlRegex: ^https://(.[^/])*\\.*\\byahoo\\.(com|com\\.(hk|tw)|co\\.jp)/\nUrlRegex: ^https://www\\.gstatic\\.cn/onebox/weather/\nUrlRegex: ^https://www\\.wireshark\\.org/\nvlog.xuite.net\nwenda.google.com.hk\nwikipedia.org\nwoeser.middle-way.net\nwww.asus.com\nwww.blockedinchina.net\nwww.hbogo.com\nwww.melauto.it\nwww.resilio.com\nwww.savetube.com\nwww.teamviewer.com\nyuming.flnet.org\n*.kkbox.com\n*.c555.me\n*.abc.net.au\nwww.moneymanagerex.org\n*.c700.me\n*.tunemymusic.com\n*.pixnet.net\n*.showmore.com\n*.socpk.com\n*.imgur.com\n*.applealmond.com\n*.c996.me\n*.x996.me\n*.navismithapis-cdn.com\n*.hxmmdd.com\n*.uc555.me\n*.x000.me\n*.careerengine.us\n*.z-lib.org\n*.zlibcdn.com\n*.zlibcdn2.com\n*.jp1lib.org\n*.luckydesigner.space\n*.ifixit.com\n*.ujjainyoga.com\n*.uc555.me\n*.x666x.me\n*.duolingo.com\n*.im.ge\n*.weiming.info\n*.ctext.org\n*.zhangsn.me\n*.gamemale.com\n*.gstatic.com\n*.doubleclick.net\n*.ahhhhfs.com\n*.gstatic.com\n*.x888x.me\n*.bing.com\n*.mirror.xyz\n*.ikoolcore.com\n*.x222x.me\n*.fengzg.net\n*.commonhealth.com.tw\n*.pdf24.org\n*.dmm-extension.com\n*.p-smith.com\n*.dmm.co.jp\n*.dmm.com\n*.360yield.com\nsslwidget.criteo.com\n*.x555x.me\n*.qnap.com\n*.m1080.com\n*.hostloc.com\n*.x333x.me\n*.daum.net\n*.githubassets.com\n*.free.com.tw\n*.yimg.com\n*.wikihow.com\n*.x567x.me\n*.dropboxstatic.com\n*.uukanshu.com\n*.crifan.com\n*.ipsw.me\n*.pacom.mil\n*.ai1080.art\n*.ab8080.vip\n*.cc12345.vip\n*.apkpure.com\n*apkmonk.com\n*.uscardforum.com\n*.kmuh.org.tw\n*.aaag.me\n*.extrabux.com\n*.docker.com\n*.webpkgcache.com\ncdn.cookielaw.org\nimage.winudf.com\nfamilyclic.hk\n*.hklii.hk\n*.vercel.app\nwww.onenote.com\n*.ccgga.me\n*.ibbb.me\n*.caaba.me\n*.r3sub.com\n*.oaistatic.com\n*.4sqi.net\n*.ghgo.xyz\n*.fanmingming.com\n*.csp.withgoogle.com\n*.redd.it\n*.redditspace.com\n*.redditmedia.com\n*.xn--sss604efuw.top\n*.javrate.com\n*.fontawesome.com\n*.eroimg.net\n*.eroterest.net\n*.missav.com\n*.googletagmanager.com\n*.kakao.com\n*.kakaocorp.com\n*.bluesky-soft.com\n*.b-cdn.net\ncopilot.microsoft.com\n*.z-lib.io\n*.z-lib.id\n*.jnn-pa.googleapis.com\n*.firebaseinstallations.googleapis.com\n*.firebase.googleapis.com\n*.hxmmdd.com\n*.heqrmudv.site\n*.translate.goog\n*.bahamut.com.tw\n*.speedtest.net\nassets.msn.cn\n*.hjwzw.com\n*.bg3.co\n*.x.com\n*.xhd.tw\n*.softonic.cn\n*.exifedit.com\n*.imgfor80.me\n*.chatgpt.com\n*.openai.com\n"
  },
  {
    "path": "dns.routes",
    "content": "223.5.5.5/32\n223.6.6.6/32\n2400:3200::1/128\n2400:3200:baba::1/128\n119.29.29.29/32\n119.28.28.28/32\n1.12.12.12/32\n120.53.53.53/32\n2402:4e00::/128\n2402:4e00:1::/128\n180.76.76.76/32\n2400:da00::6666/128\n114.114.114.114/32\n114.114.115.115/32\n180.184.1.1/32\n180.184.2.2/32\n101.226.4.6/32\n218.30.118.6/32\n123.125.81.6/32\n140.207.198.6/32\ndomain:dns.alidns.com\ndomain:doh.pub\ndomain:dot.pub\ndomain:doh.360.cn\ndomain:dot.360.cn\n"
  },
  {
    "path": "dropbox_fs_fix.patch",
    "content": "--- /pre_fix/dropbox.py 2019-06-19 03:02:17.000000000 +0800\n+++ /after/dropbox.py   2019-07-10 01:31:14.000000000 +0800\n@@ -748,6 +748,11 @@\n     return newmeth\n\n def start_dropbox():\n+    lib_path = os.path.join(DROPBOX_DIST_PATH, \"libdropbox_fs_fix.so\")\n+    if os.path.exists(lib_path):\n+        console_print(u\"\\ndropbox: load filesystem fix library\")\n+        os.environ[\"LD_PRELOAD\"] = lib_path\n+\n     if os.access(DROPBOXD_PATH, os.X_OK):\n         f = open(\"/dev/null\", \"w\")\n         # we don't reap the child because we're gonna die anyway, let init do it\n"
  },
  {
    "path": "fcm.hosts",
    "content": "127.0.0.1           localhost\n::1                 localhost\n\n\n108.177.125.188     mtalk.google.com\n64.233.188.188      alt1-mtalk.google.com\n74.125.24.188       alt2-mtalk.google.com\n74.125.200.188      alt3-mtalk.google.com\n173.194.174.188     alt4-mtalk.google.com\n172.217.194.188     alt5-mtalk.google.com\n142.250.4.188       alt6-mtalk.google.com\n142.251.175.188     alt7-mtalk.google.com\n108.177.97.188      alt8-mtalk.google.com\n\n\n2404:6800:4008:c1b::bc  mtalk.google.com\n2404:6800:4008:c01::bc  alt1-mtalk.google.com\n2404:6800:4008:c02::bc  alt2-mtalk.google.com\n2404:6800:4008:c03::bc  alt3-mtalk.google.com\n2404:6800:4003:c00::bc  alt4-mtalk.google.com\n2404:6800:4008:c07::bc  alt5-mtalk.google.com\n2404:6800:4003:c11::bc  alt6-mtalk.google.com\n2404:6800:4008:c13::bc  alt7-mtalk.google.com\n2404:6800:4008:c19::bc  alt8-mtalk.google.com\n"
  },
  {
    "path": "ffmpeg-let-rtmp-flv-support-hevc-h265-opus.patch",
    "content": "RTMP Protocol & FLV Encode/Decode Support H.265/HEVC & OPUS\n\ndiff --color -uNr a/libavformat/flvdec.c b/libavformat/flvdec.c\n--- a/libavformat/flvdec.c\t2020-07-11 18:39:30.000000000 +0800\n+++ b/libavformat/flvdec.c\t2020-12-29 19:24:42.400006406 +0800\n@@ -36,6 +36,7 @@\n #include \"internal.h\"\n #include \"avio_internal.h\"\n #include \"flv.h\"\n+#include \"hevc.h\"\n \n #define VALIDATE_INDEX_TS_THRESH 2500\n \n@@ -233,6 +234,11 @@\n     case FLV_CODECID_PCM_ALAW:\n         return apar->sample_rate == 8000 &&\n                apar->codec_id    == AV_CODEC_ID_PCM_ALAW;\n+    case FLV_CODECID_OPUS:\n+        return apar->sample_rate == 48000 &&\n+               apar->channels    == 2 &&\n+               apar->bits_per_coded_sample == 16 &&\n+               apar->codec_id    == AV_CODEC_ID_OPUS;\n     default:\n         return apar->codec_tag == (flv_codecid >> FLV_AUDIO_CODECID_OFFSET);\n     }\n@@ -291,6 +297,12 @@\n         apar->sample_rate = 8000;\n         apar->codec_id    = AV_CODEC_ID_PCM_ALAW;\n         break;\n+    case FLV_CODECID_OPUS:\n+        apar->sample_rate = 48000;\n+        apar->channels    = 2;\n+        apar->bits_per_coded_sample = 16;\n+        apar->codec_id    = AV_CODEC_ID_OPUS;\n+        break;\n     default:\n         avpriv_request_sample(s, \"Audio codec (%x)\",\n                flv_codecid >> FLV_AUDIO_CODECID_OFFSET);\n@@ -318,6 +330,8 @@\n         return vpar->codec_id == AV_CODEC_ID_VP6A;\n     case FLV_CODECID_H264:\n         return vpar->codec_id == AV_CODEC_ID_H264;\n+    case FLV_CODECID_HEVC:\n+        return vpar->codec_id == AV_CODEC_ID_HEVC;\n     default:\n         return vpar->codec_tag == flv_codecid;\n     }\n@@ -367,6 +381,11 @@\n         par->codec_id = AV_CODEC_ID_MPEG4;\n         ret = 3;\n         break;\n+    case FLV_CODECID_HEVC:\n+        par->codec_id = AV_CODEC_ID_HEVC;\n+        vstream->need_parsing = AVSTREAM_PARSE_NONE;\n+        ret = 3;\n+        break;\n     default:\n         avpriv_request_sample(s, \"Video codec (%x)\", flv_codecid);\n         par->codec_tag = flv_codecid;\n@@ -1222,7 +1241,8 @@\n \n     if (st->codecpar->codec_id == AV_CODEC_ID_AAC ||\n         st->codecpar->codec_id == AV_CODEC_ID_H264 ||\n-        st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {\n+        st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||\n+        st->codecpar->codec_id == AV_CODEC_ID_HEVC ) {\n         int type = avio_r8(s->pb);\n         size--;\n \n@@ -1231,7 +1251,7 @@\n             goto leave;\n         }\n \n-        if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {\n+        if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4 || st->codecpar->codec_id == AV_CODEC_ID_HEVC) {\n             // sign extension\n             int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;\n             pts = dts + cts;\n@@ -1247,7 +1267,7 @@\n             }\n         }\n         if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC ||\n-            st->codecpar->codec_id == AV_CODEC_ID_H264)) {\n+            st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_HEVC)) {\n             AVDictionaryEntry *t;\n \n             if (st->codecpar->extradata) {\ndiff --color -uNr a/libavformat/flvenc.c b/libavformat/flvenc.c\n--- a/libavformat/flvenc.c\t2020-07-11 18:39:30.000000000 +0800\n+++ b/libavformat/flvenc.c\t2020-12-29 19:36:34.543776338 +0800\n@@ -34,7 +34,7 @@\n #include \"libavutil/opt.h\"\n #include \"libavcodec/put_bits.h\"\n #include \"libavcodec/aacenctab.h\"\n-\n+#include \"hevc.h\"\n \n static const AVCodecTag flv_video_codec_ids[] = {\n     { AV_CODEC_ID_FLV1,     FLV_CODECID_H263 },\n@@ -46,6 +46,7 @@\n     { AV_CODEC_ID_VP6,      FLV_CODECID_VP6 },\n     { AV_CODEC_ID_VP6A,     FLV_CODECID_VP6A },\n     { AV_CODEC_ID_H264,     FLV_CODECID_H264 },\n+    { AV_CODEC_ID_HEVC,     FLV_CODECID_HEVC },\n     { AV_CODEC_ID_NONE,     0 }\n };\n \n@@ -60,6 +61,7 @@\n     { AV_CODEC_ID_PCM_MULAW,  FLV_CODECID_PCM_MULAW  >> FLV_AUDIO_CODECID_OFFSET },\n     { AV_CODEC_ID_PCM_ALAW,   FLV_CODECID_PCM_ALAW   >> FLV_AUDIO_CODECID_OFFSET },\n     { AV_CODEC_ID_SPEEX,      FLV_CODECID_SPEEX      >> FLV_AUDIO_CODECID_OFFSET },\n+    { AV_CODEC_ID_OPUS,       FLV_CODECID_OPUS       >> FLV_AUDIO_CODECID_OFFSET },\n     { AV_CODEC_ID_NONE,       0 }\n };\n \n@@ -132,6 +134,9 @@\n     if (par->codec_id == AV_CODEC_ID_AAC) // specs force these parameters\n         return FLV_CODECID_AAC | FLV_SAMPLERATE_44100HZ |\n                FLV_SAMPLESSIZE_16BIT | FLV_STEREO;\n+    else if (par->codec_id == AV_CODEC_ID_OPUS) // specs force these parameters\n+        return FLV_CODECID_OPUS | FLV_SAMPLERATE_44100HZ |\n+               FLV_SAMPLESSIZE_16BIT | FLV_STEREO;\n     else if (par->codec_id == AV_CODEC_ID_SPEEX) {\n         if (par->sample_rate != 16000) {\n             av_log(s, AV_LOG_ERROR,\n@@ -491,7 +496,7 @@\n     FLVContext *flv = s->priv_data;\n \n     if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264\n-            || par->codec_id == AV_CODEC_ID_MPEG4) {\n+            || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC) {\n         int64_t pos;\n         avio_w8(pb,\n                 par->codec_type == AVMEDIA_TYPE_VIDEO ?\n@@ -537,7 +542,11 @@\n             avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags\n             avio_w8(pb, 0); // AVC sequence header\n             avio_wb24(pb, 0); // composition time\n-            ff_isom_write_avcc(pb, par->extradata, par->extradata_size);\n+            if (par->codec_id == AV_CODEC_ID_HEVC) {\n+                ff_isom_write_hvcc(pb, par->extradata, par->extradata_size, 0);\n+            } else {\n+                ff_isom_write_avcc(pb, par->extradata, par->extradata_size);\n+            }\n         }\n         data_size = avio_tell(pb) - pos;\n         avio_seek(pb, -data_size - 10, SEEK_CUR);\n@@ -844,7 +853,7 @@\n             AVCodecParameters *par = s->streams[i]->codecpar;\n             FLVStreamContext *sc = s->streams[i]->priv_data;\n             if (par->codec_type == AVMEDIA_TYPE_VIDEO &&\n-                    (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4))\n+                    (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC))\n                 put_avc_eos_tag(pb, sc->last_ts);\n         }\n     }\n@@ -895,13 +904,13 @@\n     if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A ||\n         par->codec_id == AV_CODEC_ID_VP6  || par->codec_id == AV_CODEC_ID_AAC)\n         flags_size = 2;\n-    else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4)\n+    else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC)\n         flags_size = 5;\n     else\n         flags_size = 1;\n \n     if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264\n-            || par->codec_id == AV_CODEC_ID_MPEG4) {\n+            || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC) {\n         int side_size = 0;\n         uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);\n         if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) {\n@@ -966,6 +975,10 @@\n         if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1)\n             if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0)\n                 return ret;\n+    } else if (par->codec_id == AV_CODEC_ID_HEVC) {\n+        if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1)\n+            if ((ret = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, NULL)) < 0)\n+                return ret;\n     } else if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&\n                (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {\n         if (!s->streams[pkt->stream_index]->nb_frames) {\n@@ -1036,9 +1049,9 @@\n             else\n                 avio_w8(pb, ((FFALIGN(par->width,  16) - par->width) << 4) |\n                              (FFALIGN(par->height, 16) - par->height));\n-        } else if (par->codec_id == AV_CODEC_ID_AAC)\n+        } else if (par->codec_id == AV_CODEC_ID_AAC) {\n             avio_w8(pb, 1); // AAC raw\n-        else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) {\n+        } else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC) {\n             avio_w8(pb, 1); // AVC NALU\n             avio_wb24(pb, pkt->pts - pkt->dts);\n         }\ndiff --color -uNr a/libavformat/flv.h b/libavformat/flv.h\n--- a/libavformat/flv.h\t2020-07-09 17:17:46.000000000 +0800\n+++ b/libavformat/flv.h\t2020-12-29 19:10:08.444114140 +0800\n@@ -99,6 +99,7 @@\n     FLV_CODECID_PCM_MULAW            = 8 << FLV_AUDIO_CODECID_OFFSET,\n     FLV_CODECID_AAC                  = 10<< FLV_AUDIO_CODECID_OFFSET,\n     FLV_CODECID_SPEEX                = 11<< FLV_AUDIO_CODECID_OFFSET,\n+    FLV_CODECID_OPUS                 = 13<< FLV_AUDIO_CODECID_OFFSET,\n };\n \n enum {\n@@ -110,6 +111,7 @@\n     FLV_CODECID_H264    = 7,\n     FLV_CODECID_REALH263= 8,\n     FLV_CODECID_MPEG4   = 9,\n+    FLV_CODECID_HEVC    = 12,\n };\n \n enum {\n"
  },
  {
    "path": "gl_mt1300_led_daemon.patch",
    "content": "--- a   2022-05-20 07:38:48.486091422 +0800\n+++ b   2022-05-20 07:38:58.000336930 +0800\n@@ -1,15 +1,14 @@\n #!/bin/sh\n\n+cycle=30\n+tally=0\n status=\"0\"\n-count=`uci get mwan3.wan.count 2>/dev/null`\n-timeout=`uci get mwan3.wan.timeout 2>/dev/null`\n-track_ip=`uci get mwan3.wan.track_ip 2>/dev/null`\n-[ -z \"$count\" ] && count=\"1\"\n-[ -z \"$timeout\" ] && timeout=\"2\"\n-[ -z \"$track_ip\" ] && track_ip=\"8.8.4.4 8.8.8.8 208.67.222.222 208.67.220.220\"\n+count=\"1\"\n+timeout=\"2\"\n+track_ip=\"223.5.5.5 180.76.76.76 119.29.29.29 114.114.114.114 1.1.1.1 8.8.8.8 4.2.2.1 208.67.222.222\"\n\n mt1300_led off\n-mt1300_led blue_breath daemon\n+mt1300_led blue_flash\n\n for ip in $track_ip;\n do\n@@ -25,8 +24,19 @@\n do\n         if [ \"$status\" = \"0\" ];then\n                 mt1300_led blue_breath daemon\n+                tally=0\n         else\n-                mt1300_led white daemon\n+                if [ -d /sys/class/net/tun* ]; then\n+                        tally=0\n+                        mt1300_led both daemon\n+                else\n+                        let tally+=1\n+                        if [ $tally -lt $cycle ];then\n+                                mt1300_led white daemon\n+                        else\n+                                mt1300_led off\n+                        fi\n+                fi\n         fi\n\n         sleep 5\n"
  },
  {
    "path": "keys.dict",
    "content": "00085e2ca32c\n010203040506\n06a95de06dd5\n0aa5f10bd6d5\n0ca3eec5db81\n123456789abc\n123456abcdef\n1a2b3c4d5e6f\n1a982c7e459a\n228e01723e31\n2299b5aaf7da\n22bfbb0908d5\n4364d798e9b4\n454445ad9115\n474249434351\n475fcdff567f\n4b1c77a4d2c1\n4d3a99c351dd\n4e762c24ad1c\n533cb6c723f6\n587ee5f9350f\n5f5bfd7d4e5b\n5f7beff15e5b\n634d53dd81e3\n64d14ed565f9\n68de227c8b11\n6b3c56d6c903\n6cd44c605402\n6eaa4eac3d2a\n714c5c886e97\n76ea4eaf6b62\n782568615503\n7861609b0d88\n836dd5d2498a\n882551c9d880\n899261486a28\n8fd0a4f256e9\na0478cc39091\na0a1a2a3a4a5\na5a412d418ec\naabbccddeeff\nabcdef123456\naf88173051f5\nb03646f7ef1c\nb0b1b2b3b4b5\nb9884110200d\nbe0e3297d1c4\nc0c1c2c3c4c5\nd0141ce2327e\nd0d1d2d3d4d5\nd3f7d3f7d3f7\ndb8876f2cb27\ndde63639976e\ne1019b602902\ne1ea7b8ee45e\ne9e31129119e\neffeece2a3de\nf7f73a8f7d82\nfb7f2a19522b\nfe7bff776eff\nfeffcfff7f5b\nffdfcf7ff6df\n"
  },
  {
    "path": "nginx.patch",
    "content": "Add HTTP2 HPACK Encoding Support.\nAdd Dynamic TLS Record Support.\n\nUsing: patch -p1 < nginx.patch\n\ndiff --color -uNr a/auto/modules b/auto/modules\n--- a/auto/modules\t2023-05-23 23:08:20.000000000 +0800\n+++ b/auto/modules\t2023-05-24 00:52:16.901966472 +0800\n@@ -466,6 +466,10 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_V2_HPACK_ENC = YES ]; then\n+        have=NGX_HTTP_V2_HPACK_ENC . auto/have\n+    fi\n+\n     if :; then\n         ngx_module_name=ngx_http_static_module\n         ngx_module_incs=\ndiff --color -uNr a/auto/options b/auto/options\n--- a/auto/options\t2023-05-23 23:08:20.000000000 +0800\n+++ b/auto/options\t2023-05-24 00:54:34.532597378 +0800\n@@ -61,6 +61,7 @@\n HTTP_GZIP=YES\n HTTP_SSL=NO\n HTTP_V2=NO\n+HTTP_V2_HPACK_ENC=NO\n HTTP_V3=NO\n HTTP_SSI=YES\n HTTP_REALIP=NO\n@@ -236,6 +237,7 @@\n \n         --with-http_ssl_module)          HTTP_SSL=YES               ;;\n         --with-http_v2_module)           HTTP_V2=YES                ;;\n+        --with-http_v2_hpack_enc)        HTTP_V2_HPACK_ENC=YES      ;;\n         --with-http_v3_module)           HTTP_V3=YES                ;;\n         --with-http_realip_module)       HTTP_REALIP=YES            ;;\n         --with-http_addition_module)     HTTP_ADDITION=YES          ;;\n@@ -456,6 +458,7 @@\n \n   --with-http_ssl_module             enable ngx_http_ssl_module\n   --with-http_v2_module              enable ngx_http_v2_module\n+  --with-http_v2_hpack_enc           enable ngx_http_v2_hpack_enc\n   --with-http_v3_module              enable ngx_http_v3_module\n   --with-http_realip_module          enable ngx_http_realip_module\n   --with-http_addition_module        enable ngx_http_addition_module\ndiff --color -uNr a/src/core/ngx_murmurhash.c b/src/core/ngx_murmurhash.c\n--- a/src/core/ngx_murmurhash.c\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/core/ngx_murmurhash.c\t2023-05-24 00:52:16.902966499 +0800\n@@ -50,3 +50,63 @@\n \n     return h;\n }\n+\n+\n+uint64_t\n+ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed)\n+{\n+    uint64_t  h, k;\n+\n+    h = seed ^ len;\n+\n+    while (len >= 8) {\n+        k  = data[0];\n+        k |= data[1] << 8;\n+        k |= data[2] << 16;\n+        k |= data[3] << 24;\n+        k |= (uint64_t)data[4] << 32;\n+        k |= (uint64_t)data[5] << 40;\n+        k |= (uint64_t)data[6] << 48;\n+        k |= (uint64_t)data[7] << 56;\n+\n+        k *= 0xc6a4a7935bd1e995ull;\n+        k ^= k >> 47;\n+        k *= 0xc6a4a7935bd1e995ull;\n+\n+        h ^= k;\n+        h *= 0xc6a4a7935bd1e995ull;\n+\n+        data += 8;\n+        len -= 8;\n+    }\n+\n+    switch (len) {\n+    case 7:\n+        h ^= (uint64_t)data[6] << 48;\n+        /* fall through */\n+    case 6:\n+        h ^= (uint64_t)data[5] << 40;\n+        /* fall through */\n+    case 5:\n+        h ^= (uint64_t)data[4] << 32;\n+        /* fall through */\n+    case 4:\n+        h ^= data[3] << 24;\n+        /* fall through */\n+    case 3:\n+        h ^= data[2] << 16;\n+        /* fall through */\n+    case 2:\n+        h ^= data[1] << 8;\n+        /* fall through */\n+    case 1:\n+        h ^= data[0];\n+        h *= 0xc6a4a7935bd1e995ull;\n+    }\n+\n+    h ^= h >> 47;\n+    h *= 0xc6a4a7935bd1e995ull;\n+    h ^= h >> 47;\n+\n+    return h;\n+}\ndiff --color -uNr a/src/core/ngx_murmurhash.h b/src/core/ngx_murmurhash.h\n--- a/src/core/ngx_murmurhash.h\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/core/ngx_murmurhash.h\t2023-05-24 00:52:16.903966525 +0800\n@@ -15,5 +15,7 @@\n \n uint32_t ngx_murmur_hash2(u_char *data, size_t len);\n \n+uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed);\n+\n \n #endif /* _NGX_MURMURHASH_H_INCLUDED_ */\ndiff --color -uNr a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c\n--- a/src/event/ngx_event_openssl.c\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/event/ngx_event_openssl.c\t2023-05-24 00:52:16.905966578 +0800\n@@ -1674,6 +1674,7 @@\n \n     sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);\n     sc->buffer_size = ssl->buffer_size;\n+    sc->dyn_rec = ssl->dyn_rec;\n \n     sc->session_ctx = ssl->ctx;\n \n@@ -2645,6 +2646,41 @@\n \n     for ( ;; ) {\n \n+        /* Dynamic record resizing:\n+           We want the initial records to fit into one TCP segment\n+           so we don't get TCP HoL blocking due to TCP Slow Start.\n+           A connection always starts with small records, but after\n+           a given amount of records sent, we make the records larger\n+           to reduce header overhead.\n+           After a connection has idled for a given timeout, begin\n+           the process from the start. The actual parameters are\n+           configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */\n+\n+        if (c->ssl->dyn_rec.timeout > 0 ) {\n+\n+            if (ngx_current_msec - c->ssl->dyn_rec_last_write >\n+                c->ssl->dyn_rec.timeout)\n+            {\n+                buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                c->ssl->dyn_rec_records_sent = 0;\n+\n+            } else {\n+                if (c->ssl->dyn_rec_records_sent >\n+                    c->ssl->dyn_rec.threshold * 2)\n+                {\n+                    buf->end = buf->start + c->ssl->buffer_size;\n+\n+                } else if (c->ssl->dyn_rec_records_sent >\n+                           c->ssl->dyn_rec.threshold)\n+                {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_hi;\n+\n+                } else {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                }\n+            }\n+        }\n+\n         while (in && buf->last < buf->end && send < limit) {\n             if (in->buf->last_buf || in->buf->flush) {\n                 flush = 1;\n@@ -2784,6 +2820,9 @@\n \n     if (n > 0) {\n \n+        c->ssl->dyn_rec_records_sent++;\n+        c->ssl->dyn_rec_last_write = ngx_current_msec;\n+\n         if (c->ssl->saved_read_handler) {\n \n             c->read->handler = c->ssl->saved_read_handler;\ndiff --color -uNr a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h\n--- a/src/event/ngx_event_openssl.h\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/event/ngx_event_openssl.h\t2023-05-24 00:52:16.906966604 +0800\n@@ -86,10 +86,19 @@\n typedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;\n \n \n+typedef struct {\n+    ngx_msec_t                  timeout;\n+    ngx_uint_t                  threshold;\n+    size_t                      size_lo;\n+    size_t                      size_hi;\n+} ngx_ssl_dyn_rec_t;\n+\n+\n struct ngx_ssl_s {\n     SSL_CTX                    *ctx;\n     ngx_log_t                  *log;\n     size_t                      buffer_size;\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n };\n \n \n@@ -128,6 +137,10 @@\n     unsigned                    in_ocsp:1;\n     unsigned                    early_preread:1;\n     unsigned                    write_blocked:1;\n+\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n+    ngx_msec_t                  dyn_rec_last_write;\n+    ngx_uint_t                  dyn_rec_records_sent;\n };\n \n \n@@ -137,7 +150,7 @@\n #define NGX_SSL_DFLT_BUILTIN_SCACHE  -5\n \n \n-#define NGX_SSL_MAX_SESSION_SIZE  4096\n+#define NGX_SSL_MAX_SESSION_SIZE  16384\n \n typedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c\n--- a/src/http/modules/ngx_http_ssl_module.c\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.c\t2023-05-24 00:52:16.906966604 +0800\n@@ -304,6 +304,41 @@\n       offsetof(ngx_http_ssl_srv_conf_t, reject_handshake),\n       NULL },\n \n+    { ngx_string(\"ssl_dyn_rec_enable\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_flag_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_lo\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_hi\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_threshold\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold),\n+      NULL },\n+\n       ngx_null_command\n };\n \n@@ -636,6 +671,11 @@\n     sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR;\n     sscf->stapling = NGX_CONF_UNSET;\n     sscf->stapling_verify = NGX_CONF_UNSET;\n+    sscf->dyn_rec_enable = NGX_CONF_UNSET;\n+    sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC;\n+    sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT;\n \n     return sscf;\n }\n@@ -712,6 +752,20 @@\n     ngx_conf_merge_str_value(conf->stapling_responder,\n                          prev->stapling_responder, \"\");\n \n+    ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0);\n+    ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout,\n+                             1000);\n+    /* Default sizes for the dynamic record sizes are defined to fit maximal\n+       TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi:\n+       1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo,\n+                             1369);\n+    /* 4229 = (1500 - 40 - 20 - 10) * 3  - 61 */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi,\n+                             4229);\n+    ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold,\n+                             40);\n+\n     conf->ssl.log = cf->log;\n \n     if (conf->enable) {\n@@ -938,6 +992,28 @@\n         return NGX_CONF_ERROR;\n     }\n \n+    if (conf->dyn_rec_enable) {\n+        conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout;\n+        conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold;\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_lo) {\n+            conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_lo = conf->buffer_size;\n+        }\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_hi) {\n+            conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_hi = conf->buffer_size;\n+        }\n+\n+    } else {\n+        conf->ssl.dyn_rec.timeout = 0;\n+    }\n+\n     return NGX_CONF_OK;\n }\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h\n--- a/src/http/modules/ngx_http_ssl_module.h\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.h\t2023-05-24 00:52:16.907966630 +0800\n@@ -67,6 +67,12 @@\n \n     u_char                         *file;\n     ngx_uint_t                      line;\n+\n+    ngx_flag_t                      dyn_rec_enable;\n+    ngx_msec_t                      dyn_rec_timeout;\n+    size_t                          dyn_rec_size_lo;\n+    size_t                          dyn_rec_size_hi;\n+    ngx_uint_t                      dyn_rec_threshold;\n } ngx_http_ssl_srv_conf_t;\n \n \ndiff --color -uNr a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c\n--- a/src/http/v2/ngx_http_v2.c\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.c\t2023-05-24 00:52:16.909966683 +0800\n@@ -274,6 +274,8 @@\n \n     h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n \n+    h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE;\n+\n     h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n \n     h2c->concurrent_pushes = h2scf->concurrent_pushes;\n@@ -2280,6 +2282,14 @@\n         case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:\n \n             h2c->table_update = 1;\n+\n+            if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+                h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE;\n+            } else {\n+                h2c->max_hpack_table_size = value;\n+            }\n+\n+            h2c->indicate_resize = 1;\n             break;\n \n         default:\ndiff --color -uNr a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c\n--- a/src/http/v2/ngx_http_v2_encode.c\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_encode.c\t2023-05-24 00:52:16.909966683 +0800\n@@ -10,7 +10,7 @@\n #include <ngx_http.h>\n \n \n-static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n+u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n     ngx_uint_t value);\n \n \n@@ -40,7 +40,7 @@\n }\n \n \n-static u_char *\n+u_char *\n ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)\n {\n     if (value < prefix) {\ndiff --color -uNr a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c\n--- a/src/http/v2/ngx_http_v2_filter_module.c\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_filter_module.c\t2023-05-24 00:52:16.910966710 +0800\n@@ -23,10 +23,53 @@\n #define ngx_http_v2_literal_size(h)                                           \\\n     (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)\n \n+#define ngx_http_v2_indexed(i)      (128 + (i))\n+#define ngx_http_v2_inc_indexed(i)  (64 + (i))\n+\n+#define NGX_HTTP_V2_ENCODE_RAW            0\n+#define NGX_HTTP_V2_ENCODE_HUFF           0x80\n+\n+#define NGX_HTTP_V2_AUTHORITY_INDEX       1\n+#define NGX_HTTP_V2_METHOD_GET_INDEX      2\n+#define NGX_HTTP_V2_PATH_INDEX            4\n+\n+#define NGX_HTTP_V2_SCHEME_HTTP_INDEX     6\n+#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX    7\n+\n+#define NGX_HTTP_V2_STATUS_INDEX          8\n+#define NGX_HTTP_V2_STATUS_200_INDEX      8\n+#define NGX_HTTP_V2_STATUS_204_INDEX      9\n+#define NGX_HTTP_V2_STATUS_206_INDEX      10\n+#define NGX_HTTP_V2_STATUS_304_INDEX      11\n+#define NGX_HTTP_V2_STATUS_400_INDEX      12\n+#define NGX_HTTP_V2_STATUS_404_INDEX      13\n+#define NGX_HTTP_V2_STATUS_500_INDEX      14\n+\n+#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16\n+#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17\n+#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX  28\n+#define NGX_HTTP_V2_CONTENT_TYPE_INDEX    31\n+#define NGX_HTTP_V2_DATE_INDEX            33\n+#define NGX_HTTP_V2_LAST_MODIFIED_INDEX   44\n+#define NGX_HTTP_V2_LOCATION_INDEX        46\n+#define NGX_HTTP_V2_SERVER_INDEX          54\n+#define NGX_HTTP_V2_USER_AGENT_INDEX      58\n+#define NGX_HTTP_V2_VARY_INDEX            59\n \n #define NGX_HTTP_V2_NO_TRAILERS           (ngx_http_v2_out_frame_t *) -1\n \n \n+static const struct {\n+    u_char        *name;\n+    u_char const   len;\n+} push_header[] = {\n+    { (u_char*)\":authority\"      , 10 },\n+    { (u_char*)\"accept-encoding\" , 15 },\n+    { (u_char*)\"accept-language\" , 15 },\n+    { (u_char*)\"user-agent\"      , 10 }\n+};\n+\n+\n typedef struct {\n     ngx_str_t      name;\n     u_char         index;\n@@ -155,11 +198,9 @@\n #endif\n \n     static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);\n-    static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];\n \n     static size_t nginx_ver_build_len =\n                                   ngx_http_v2_literal_size(NGINX_VER_BUILD);\n-    static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];\n \n     stream = r->stream;\n \n@@ -435,7 +476,7 @@\n     }\n \n     tmp = ngx_palloc(r->pool, tmp_len);\n-    pos = ngx_pnalloc(r->pool, len);\n+    pos = ngx_pnalloc(r->pool, len + 15 + 1);\n \n     if (pos == NULL || tmp == NULL) {\n         return NGX_ERROR;\n@@ -443,11 +484,16 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n     }\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n@@ -458,67 +504,28 @@\n         *pos++ = status;\n \n     } else {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);\n-        *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;\n-        pos = ngx_sprintf(pos, \"%03ui\", r->headers_out.status);\n+        ngx_sprintf(pos + 8, \"%O3ui\", r->headers_out.status);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\":status\",\n+                                       sizeof(\":status\") - 1, pos + 8, 3, tmp);\n     }\n \n     if (r->headers_out.server == NULL) {\n-\n         if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER);\n-\n-        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER_BUILD);\n-\n-        } else {\n-            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: nginx\\\"\");\n-        }\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);\n-\n-        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            if (nginx_ver[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,\n-                                            sizeof(NGINX_VER) - 1, tmp);\n-                nginx_ver_len = p - nginx_ver;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER);\n \n         } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            if (nginx_ver_build[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver_build,\n-                                            (u_char *) NGINX_VER_BUILD,\n-                                            sizeof(NGINX_VER_BUILD) - 1, tmp);\n-                nginx_ver_build_len = p - nginx_ver_build;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER_BUILD);\n \n         } else {\n-            pos = ngx_cpymem(pos, nginx, sizeof(nginx));\n+            pos = ngx_http_v2_write_header_str(\"server\", \"nginx\");\n         }\n     }\n \n     if (r->headers_out.date == NULL) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"date: %V\\\"\",\n-                       &ngx_cached_http_time);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);\n-        pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,\n-                                      ngx_cached_http_time.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"date\", ngx_cached_http_time);\n     }\n \n     if (r->headers_out.content_type.len) {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);\n-\n         if (r->headers_out.content_type_len == r->headers_out.content_type.len\n             && r->headers_out.charset.len)\n         {\n@@ -544,64 +551,36 @@\n             r->headers_out.content_type.data = p - len;\n         }\n \n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-type: %V\\\"\",\n-                       &r->headers_out.content_type);\n-\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,\n-                                      r->headers_out.content_type.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"content-type\",\n+                                           r->headers_out.content_type);\n     }\n \n     if (r->headers_out.content_length == NULL\n         && r->headers_out.content_length_n >= 0)\n     {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-length: %O\\\"\",\n-                       r->headers_out.content_length_n);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);\n-\n-        p = pos;\n-        pos = ngx_sprintf(pos + 1, \"%O\", r->headers_out.content_length_n);\n-        *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);\n+        p = ngx_sprintf(pos + 15, \"%O\", r->headers_out.content_length_n);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"content-length\",\n+                                       sizeof(\"content-length\") - 1, pos + 15,\n+                                       p - (pos + 15), tmp);\n     }\n \n     if (r->headers_out.last_modified == NULL\n         && r->headers_out.last_modified_time != -1)\n     {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);\n-\n-        ngx_http_time(pos, r->headers_out.last_modified_time);\n+        ngx_http_time(pos + 14, r->headers_out.last_modified_time);\n         len = sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1;\n-\n-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"last-modified: %*s\\\"\",\n-                       len, pos);\n-\n-        /*\n-         * Date will always be encoded using huffman in the temporary buffer,\n-         * so it's safe here to use src and dst pointing to the same address.\n-         */\n-        pos = ngx_http_v2_write_value(pos, pos, len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"last-modified\",\n+                                       sizeof(\"last-modified\") - 1, pos + 14,\n+                                       len, tmp);\n     }\n \n     if (r->headers_out.location && r->headers_out.location->value.len) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"location: %V\\\"\",\n-                       &r->headers_out.location->value);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,\n-                                      r->headers_out.location->value.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"location\", r->headers_out.location->value);\n     }\n \n #if (NGX_HTTP_GZIP)\n     if (r->gzip_vary) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"vary: Accept-Encoding\\\"\");\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);\n-        pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));\n+        pos = ngx_http_v2_write_header_str(\"vary\", \"Accept-Encoding\");\n     }\n #endif\n \n@@ -624,23 +603,10 @@\n             continue;\n         }\n \n-#if (NGX_DEBUG)\n-        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n-            ngx_strlow(tmp, header[i].key.data, header[i].key.len);\n-\n-            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"%*s: %V\\\"\",\n-                           header[i].key.len, tmp, &header[i].value);\n-        }\n-#endif\n-\n-        *pos++ = 0;\n-\n-        pos = ngx_http_v2_write_name(pos, header[i].key.data,\n-                                     header[i].key.len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data,\n+                                       header[i].key.len, header[i].value.data,\n+                                       header[i].value.len, tmp);\n \n-        pos = ngx_http_v2_write_value(pos, header[i].value.data,\n-                                      header[i].value.len, tmp);\n     }\n \n     fin = r->header_only\n@@ -997,6 +963,7 @@\n \n     for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n         len += binary[i].len;\n+        len += push_header[i].len + 1;\n     }\n \n     pos = ngx_pnalloc(r->pool, len);\n@@ -1006,12 +973,17 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n-    }\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n+     }\n \n     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":method: GET\\\"\");\n@@ -1021,8 +993,7 @@\n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":path: %V\\\"\", path);\n \n-    *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n-    pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);\n+    pos = ngx_http_v2_write_header_pot(\":path\", path);\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":scheme: %V\\\"\", &r->schema);\n@@ -1047,11 +1018,15 @@\n             continue;\n         }\n \n+        value = &(*h)->value;\n+\n         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                        \"http2 push header: \\\"%V: %V\\\"\",\n                        &ph[i].name, &(*h)->value);\n \n-        pos = ngx_cpymem(pos, binary[i].data, binary[i].len);\n+        pos = ngx_http_v2_write_header(h2c, pos,\n+                  push_header[i].name, push_header[i].len, value->data, value->len,\n+                  tmp);\n     }\n \n     frame = ngx_http_v2_create_push_frame(r, start, pos);\ndiff --color -uNr a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h\n--- a/src/http/v2/ngx_http_v2.h\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.h\t2023-05-24 00:52:16.911966736 +0800\n@@ -51,6 +51,14 @@\n #define NGX_HTTP_V2_MAX_WINDOW           ((1U << 31) - 1)\n #define NGX_HTTP_V2_DEFAULT_WINDOW       65535\n \n+#define HPACK_ENC_HTABLE_SZ              128 /* better to keep a PoT < 64k */\n+#define HPACK_ENC_HTABLE_ENTRIES         ((HPACK_ENC_HTABLE_SZ * 100) / 128)\n+#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ     10  /* 10 is sufficient for most */\n+#define HPACK_ENC_MAX_ENTRY              512 /* longest header size to match */\n+\n+#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE     4096\n+#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE         16384 /* < 64k */\n+\n #define NGX_HTTP_V2_DEFAULT_WEIGHT       16\n \n \n@@ -114,6 +122,46 @@\n } ngx_http_v2_hpack_t;\n \n \n+#if (NGX_HTTP_V2_HPACK_ENC)\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen, vlen;\n+    uint16_t                         size;\n+    uint16_t                         next;\n+} ngx_http_v2_hpack_enc_entry_t;\n+\n+\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen;\n+} ngx_http_v2_hpack_name_entry_t;\n+\n+\n+typedef struct {\n+    size_t                           size;    /* size as defined in RFC 7541 */\n+    uint32_t                         top;     /* the last entry */\n+    uint32_t                         pos;\n+    uint16_t                         n_elems; /* number of elements */\n+    uint16_t                         base;    /* index of the oldest entry */\n+    uint16_t                         last;    /* index of the newest entry */\n+\n+    /* hash table for dynamic entries, instead using a generic hash table,\n+       which would be too slow to process a significant amount of headers,\n+       this table is not determenistic, and might ocasionally fail to insert\n+       a value, at the cost of slightly worse compression, but significantly\n+       faster performance */\n+    ngx_http_v2_hpack_enc_entry_t    htable[HPACK_ENC_HTABLE_SZ];\n+    ngx_http_v2_hpack_name_entry_t   heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ];\n+    u_char                           storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE +\n+                                             HPACK_ENC_MAX_ENTRY];\n+} ngx_http_v2_hpack_enc_t;\n+#endif\n+\n+\n struct ngx_http_v2_connection_s {\n     ngx_connection_t                *connection;\n     ngx_http_connection_t           *http_connection;\n@@ -135,6 +183,8 @@\n \n     size_t                           frame_size;\n \n+    size_t                           max_hpack_table_size;\n+\n     ngx_queue_t                      waiting;\n \n     ngx_http_v2_state_t              state;\n@@ -164,6 +214,11 @@\n     unsigned                         blocked:1;\n     unsigned                         goaway:1;\n     unsigned                         push_disabled:1;\n+    unsigned                         indicate_resize:1;\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+    ngx_http_v2_hpack_enc_t          hpack_enc;\n+#endif\n };\n \n \n@@ -207,6 +262,8 @@\n \n     ngx_array_t                     *cookies;\n \n+    size_t                           header_limit;\n+\n     ngx_pool_t                      *pool;\n \n     unsigned                         waiting:1;\n@@ -413,4 +470,35 @@\n     u_char *tmp, ngx_uint_t lower);\n \n \n+u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,\n+    u_char *tmp, ngx_uint_t lower);\n+\n+u_char *\n+ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value);\n+\n+#define ngx_http_v2_write_name(dst, src, len, tmp)                            \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 1)\n+#define ngx_http_v2_write_value(dst, src, len, tmp)                           \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 0)\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+    u_char *key, size_t key_len, u_char *value, size_t value_len,\n+    u_char *tmp);\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c);\n+\n+#define ngx_http_v2_write_header_str(key, value)                        \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    (u_char *) value, sizeof(value) - 1, tmp);\n+\n+#define ngx_http_v2_write_header_tbl(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val.data, val.len, tmp);\n+\n+#define ngx_http_v2_write_header_pot(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val->data, val->len, tmp);\n+\n #endif /* _NGX_HTTP_V2_H_INCLUDED_ */\ndiff --color -uNr a/src/http/v2/ngx_http_v2_table.c b/src/http/v2/ngx_http_v2_table.c\n--- a/src/http/v2/ngx_http_v2_table.c\t2023-05-23 23:08:20.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_table.c\t2023-05-24 00:52:16.912966762 +0800\n@@ -361,3 +361,434 @@\n \n     return NGX_OK;\n }\n+\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len);\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len);\n+\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c)\n+{\n+    ngx_http_v2_hpack_enc_entry_t  *table;\n+    uint64_t                        idx;\n+\n+    table = h2c->hpack_enc.htable;\n+\n+    while (h2c->hpack_enc.size > h2c->max_hpack_table_size) {\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+}\n+\n+\n+/* checks if a header is in the hpack table - if so returns the table entry,\n+   otherwise encodes and inserts into the table and returns 0,\n+   if failed to insert into table, returns -1 */\n+static ngx_int_t\n+ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c,\n+    size_t key_len, size_t val_len, uint8_t *key, uint8_t *val,\n+    ngx_int_t *header_idx)\n+{\n+    uint64_t  hash_val, key_hash, idx, lru;\n+    int       i;\n+    size_t    size = key_len + val_len + 32;\n+    uint8_t  *storage = h2c->hpack_enc.storage;\n+\n+    ngx_http_v2_hpack_enc_entry_t   *table;\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+\n+    *header_idx = NGX_ERROR;\n+    /* step 1: compute the hash value of header */\n+    if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) {\n+        return NGX_ERROR;\n+    }\n+\n+    key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234);\n+    hash_val = ngx_murmur_hash2_64(val, val_len, key_hash);\n+\n+    if (hash_val == 0) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* step 2: check if full header in the table */\n+    idx = hash_val;\n+    i = -1;\n+    while (idx) {\n+         /* at most 8 locations are checked, but most will be done in 1 or 2 */\n+        table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ];\n+        if (table->hash_val == hash_val\n+            && table->klen == key_len\n+            && table->vlen == val_len\n+            && ngx_memcmp(key, storage + table->pos, key_len) == 0\n+            && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0)\n+        {\n+            return (h2c->hpack_enc.top - table->index) + 61;\n+        }\n+\n+        if (table->hash_val == 0 && i == -1) {\n+            i = idx % HPACK_ENC_HTABLE_SZ;\n+            break;\n+        }\n+\n+        idx >>= 8;\n+    }\n+\n+    /* step 3: check if key is in one of the tables */\n+    *header_idx = hpack_get_static_index(h2c, key, key_len);\n+\n+    if (i == -1) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (*header_idx == NGX_ERROR) {\n+        *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len);\n+    }\n+\n+    /* step 4: store the new entry */\n+    table =  h2c->hpack_enc.htable;\n+\n+    if (h2c->hpack_enc.top == 0xffffffff) {\n+        /* just to be on the safe side, avoid overflow */\n+        ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t));\n+    }\n+\n+    while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size)\n+           || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) {\n+        /* make space for the new entry first */\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+\n+    table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val,\n+                                               .index = h2c->hpack_enc.top,\n+                                               .pos = h2c->hpack_enc.pos,\n+                                               .klen = key_len,\n+                                               .vlen = val_len,\n+                                               .size = size,\n+                                               .next = 0};\n+\n+    table[h2c->hpack_enc.last].next = i;\n+    if (h2c->hpack_enc.n_elems == 0) {\n+        h2c->hpack_enc.base = i;\n+    }\n+\n+    h2c->hpack_enc.last = i;\n+    h2c->hpack_enc.top++;\n+    h2c->hpack_enc.size += size;\n+    h2c->hpack_enc.n_elems++;\n+\n+    /* update header name lookup */\n+    if (*header_idx == NGX_ERROR ) {\n+        lru = h2c->hpack_enc.top;\n+\n+        for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+\n+            name = &h2c->hpack_enc.heads[i];\n+\n+            if ( name->hash_val == 0 || (name->hash_val == key_hash\n+                && ngx_memcmp(storage + name->pos, key, key_len) == 0) )\n+            {\n+                name->hash_val = key_hash;\n+                name->pos = h2c->hpack_enc.pos;\n+                name->index = h2c->hpack_enc.top - 1;\n+                break;\n+            }\n+\n+            if (lru > name->index) {\n+                lru = name->index;\n+                idx = i;\n+            }\n+        }\n+\n+        if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) {\n+            name = &h2c->hpack_enc.heads[idx];\n+            name->hash_val = hash_val;\n+            name->pos = h2c->hpack_enc.pos;\n+            name->index = h2c->hpack_enc.top - 1;\n+        }\n+    }\n+\n+    ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len);\n+    ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len);\n+\n+    h2c->hpack_enc.pos += size;\n+    if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+        h2c->hpack_enc.pos = 0;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_int_t idx, header_idx;\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    /* attempt to find the value in the dynamic table */\n+    idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value,\n+                                           &header_idx);\n+\n+    if (idx > 0) {\n+        /* positive index indicates success */\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                       \"http2 hpack encode: Indexed Header Field: %ud\", idx);\n+\n+        *pos = 128;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx);\n+\n+    } else {\n+\n+        if (header_idx == NGX_ERROR) { /* if key is not present */\n+\n+            if (idx == NGX_ERROR) {    /* if header was not added */\n+                *pos++ = 0;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — New Name\");\n+            } else {                   /* if header was added */\n+                *pos++ = 64;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — New Name\");\n+            }\n+\n+            pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+\n+        } else {                       /* if key is present */\n+\n+            if (idx == NGX_ERROR) {\n+                *pos = 0;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — Indexed Name: %ud\", header_idx);\n+            } else {\n+                *pos = 64;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — Indexed Name: %ud\", header_idx);\n+            }\n+        }\n+\n+        pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+    }\n+\n+    return pos;\n+}\n+\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len)\n+{\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+    int                              i;\n+\n+    for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+        name = &h2c->hpack_enc.heads[i];\n+\n+        if (name->hash_val == key_hash\n+            && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0)\n+        {\n+            if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) {\n+                return (h2c->hpack_enc.top - name->index) + 61;\n+            }\n+            break;\n+        }\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+\n+/* decide if a given header is present in the static dictionary, this could be\n+   done in several ways, but it seems the fastest one is \"exhaustive\" search */\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len)\n+{\n+    /* the static dictionary of response only headers,\n+       although response headers can be put by origin,\n+       that would be rare */\n+    static const struct {\n+        u_char         len;\n+        const u_char   val[28];\n+        u_char         idx;\n+    } server_headers[] = {\n+        { 3, \"age\",                         21},//0\n+        { 3, \"via\",                         60},\n+        { 4, \"date\",                        33},//2\n+        { 4, \"etag\",                        34},\n+        { 4, \"link\",                        45},\n+        { 4, \"vary\",                        59},\n+        { 5, \"allow\",                       22},//6\n+        { 6, \"server\",                      54},//7\n+        { 7, \"expires\",                     36},//8\n+        { 7, \"refresh\",                     52},\n+        { 8, \"location\",                    46},//10\n+        {10, \"set-cookie\",                  55},//11\n+        {11, \"retry-after\",                 53},//12\n+        {12, \"content-type\",                31},//13\n+        {13, \"content-range\",               30},//14\n+        {13, \"accept-ranges\",               18},\n+        {13, \"cache-control\",               24},\n+        {13, \"last-modified\",               44},\n+        {14, \"content-length\",              28},//18\n+        {16, \"content-encoding\",            26},//19\n+        {16, \"content-language\",            27},\n+        {16, \"content-location\",            29},\n+        {16, \"www-authenticate\",            61},\n+        {17, \"transfer-encoding\",           57},//23\n+        {18, \"proxy-authenticate\",          48},//24\n+        {19, \"content-disposition\",         25},//25\n+        {25, \"strict-transport-security\",   56},//26\n+        {27, \"access-control-allow-origin\", 20},//27\n+        {99, \"\",                            99},\n+    }, *header;\n+\n+    /* for a given length, where to start the search\n+       since minimal length is 3, the table has a -3\n+       offset */\n+    static const int8_t start_at[] = {\n+        [3-3]  = 0,\n+        [4-3]  = 2,\n+        [5-3]  = 6,\n+        [6-3]  = 7,\n+        [7-3]  = 8,\n+        [8-3]  = 10,\n+        [9-3]  = -1,\n+        [10-3] = 11,\n+        [11-3] = 12,\n+        [12-3] = 13,\n+        [13-3] = 14,\n+        [14-3] = 18,\n+        [15-3] = -1,\n+        [16-3] = 19,\n+        [17-3] = 23,\n+        [18-3] = 24,\n+        [19-3] = 25,\n+        [20-3] = -1,\n+        [21-3] = -1,\n+        [22-3] = -1,\n+        [23-3] = -1,\n+        [24-3] = -1,\n+        [25-3] = 26,\n+        [26-3] = -1,\n+        [27-3] = 27,\n+    };\n+\n+    uint64_t pref;\n+    size_t   save_len = len, i;\n+    int8_t   start;\n+\n+    /* early exit for out of bounds lengths */\n+    if (len < 3 || len > 27) {\n+        return NGX_ERROR;\n+    }\n+\n+    start = start_at[len - 3];\n+    if (start == -1) {\n+        /* exit for non existent lengths */\n+        return NGX_ERROR;\n+    }\n+\n+    header = &server_headers[start_at[len - 3]];\n+\n+    /* load first 8 bytes of key, for fast comparison */\n+    if (len < 8) {\n+        pref = 0;\n+        if (len >= 4) {\n+            pref = *(uint32_t *)(val + len - 4) | 0x20202020;\n+            len -= 4;\n+        }\n+        while (len > 0) { /* 3 iterations at most */\n+            pref = (pref << 8) ^ (val[len - 1] | 0x20);\n+            len--;\n+        }\n+    } else {\n+        pref = *(uint64_t *)val | 0x2020202020202020;\n+        len -= 8;\n+    }\n+\n+    /* iterate over headers with the right length */\n+    while (header->len == save_len) {\n+        /* quickly compare the first 8 bytes, most tests will end here */\n+        if (pref != *(uint64_t *) header->val) {\n+            header++;\n+            continue;\n+        }\n+\n+        if (len == 0) {\n+            /* len == 0, indicates prefix held the entire key */\n+            return header->idx;\n+        }\n+        /* for longer keys compare the rest */\n+        i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */\n+\n+        while (i + 8 <= save_len) { /* 3 iterations at most */\n+            if ( *(uint64_t *)&header->val[i]\n+                 != (*(uint64_t *) &val[i]| 0x2020202020202020) )\n+            {\n+                header++;\n+                i = 0;\n+                break;\n+            }\n+            i += 8;\n+        }\n+\n+        if (i == 0) {\n+            continue;\n+        }\n+\n+        /* found the corresponding entry in the static dictionary */\n+        return header->idx;\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+#else\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    *pos++ = 64;\n+    pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+    pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+\n+    return pos;\n+}\n+\n+#endif\n"
  },
  {
    "path": "nginx_dynamic_tls_records.patch",
    "content": "What we do now:\nWe use a static record size of 4K. This gives a good balance of latency and\nthroughput.\n\nOptimize latency:\nBy initialy sending small (1 TCP segment) sized records, we are able to avoid\nHoL blocking of the first byte. This means TTFB is sometime lower by a whole\nRTT.\n\nOptimizing throughput:\nBy sending increasingly larger records later in the connection, when HoL is not\na problem, we reduce the overhead of TLS record (29 bytes per record with\nGCM/CHACHA-POLY).\n\nLogic:\nStart each connection with small records (1369 byte default, change with\nssl_dyn_rec_size_lo). After a given number of records (40, change with\nssl_dyn_rec_threshold) start sending larger records (4229, ssl_dyn_rec_size_hi).\nEventually after the same number of records, start sending the largest records\n(ssl_buffer_size).\nIn case the connection idles for a given amount of time (1s,\nssl_dyn_rec_timeout), the process repeats itself (i.e. begin sending small\nrecords again).\n\n\ndiff --color -uNr a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c\n--- a/src/event/ngx_event_openssl.c\t2025-04-16 20:01:11.000000000 +0800\n+++ b/src/event/ngx_event_openssl.c\t2025-04-17 02:43:15.616511714 +0800\n@@ -1620,6 +1620,7 @@\n \n     sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);\n     sc->buffer_size = ssl->buffer_size;\n+    sc->dyn_rec = ssl->dyn_rec;\n \n     sc->session_ctx = ssl->ctx;\n \n@@ -2591,6 +2592,41 @@\n \n     for ( ;; ) {\n \n+        /* Dynamic record resizing:\n+           We want the initial records to fit into one TCP segment\n+           so we don't get TCP HoL blocking due to TCP Slow Start.\n+           A connection always starts with small records, but after\n+           a given amount of records sent, we make the records larger\n+           to reduce header overhead.\n+           After a connection has idled for a given timeout, begin\n+           the process from the start. The actual parameters are\n+           configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */\n+\n+        if (c->ssl->dyn_rec.timeout > 0 ) {\n+\n+            if (ngx_current_msec - c->ssl->dyn_rec_last_write >\n+                c->ssl->dyn_rec.timeout)\n+            {\n+                buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                c->ssl->dyn_rec_records_sent = 0;\n+\n+            } else {\n+                if (c->ssl->dyn_rec_records_sent >\n+                    c->ssl->dyn_rec.threshold * 2)\n+                {\n+                    buf->end = buf->start + c->ssl->buffer_size;\n+\n+                } else if (c->ssl->dyn_rec_records_sent >\n+                           c->ssl->dyn_rec.threshold)\n+                {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_hi;\n+\n+                } else {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                }\n+            }\n+        }\n+\n         while (in && buf->last < buf->end && send < limit) {\n             if (in->buf->last_buf || in->buf->flush) {\n                 flush = 1;\n@@ -2730,6 +2766,9 @@\n \n     if (n > 0) {\n \n+        c->ssl->dyn_rec_records_sent++;\n+        c->ssl->dyn_rec_last_write = ngx_current_msec;\n+\n         if (c->ssl->saved_read_handler) {\n \n             c->read->handler = c->ssl->saved_read_handler;\ndiff --color -uNr a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h\n--- a/src/event/ngx_event_openssl.h\t2025-04-16 20:01:11.000000000 +0800\n+++ b/src/event/ngx_event_openssl.h\t2025-04-17 02:44:10.578945187 +0800\n@@ -86,6 +86,14 @@\n typedef struct ngx_ssl_ocsp_s   ngx_ssl_ocsp_t;\n \n \n+typedef struct {\n+    ngx_msec_t                  timeout;\n+    ngx_uint_t                  threshold;\n+    size_t                      size_lo;\n+    size_t                      size_hi;\n+} ngx_ssl_dyn_rec_t;\n+\n+\n struct ngx_ssl_s {\n     SSL_CTX                    *ctx;\n     ngx_log_t                  *log;\n@@ -95,6 +103,8 @@\n \n     ngx_rbtree_t                staple_rbtree;\n     ngx_rbtree_node_t           staple_sentinel;\n+\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n };\n \n \n@@ -133,6 +143,10 @@\n     unsigned                    early_preread:1;\n     unsigned                    write_blocked:1;\n     unsigned                    sni_accepted:1;\n+\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n+    ngx_msec_t                  dyn_rec_last_write;\n+    ngx_uint_t                  dyn_rec_records_sent;\n };\n \n \n@@ -142,7 +156,7 @@\n #define NGX_SSL_DFLT_BUILTIN_SCACHE  -5\n \n \n-#define NGX_SSL_MAX_SESSION_SIZE  8192\n+#define NGX_SSL_MAX_SESSION_SIZE  16384\n \n typedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c\n--- a/src/http/modules/ngx_http_ssl_module.c\t2025-04-16 20:01:11.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.c\t2025-04-17 02:43:15.618511766 +0800\n@@ -299,6 +299,41 @@\n       offsetof(ngx_http_ssl_srv_conf_t, reject_handshake),\n       NULL },\n \n+    { ngx_string(\"ssl_dyn_rec_enable\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_flag_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_lo\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_hi\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_threshold\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold),\n+      NULL },\n+\n       ngx_null_command\n };\n \n@@ -639,6 +674,11 @@\n     sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR;\n     sscf->stapling = NGX_CONF_UNSET;\n     sscf->stapling_verify = NGX_CONF_UNSET;\n+    sscf->dyn_rec_enable = NGX_CONF_UNSET;\n+    sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC;\n+    sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT;\n \n     return sscf;\n }\n@@ -705,6 +745,20 @@\n     ngx_conf_merge_str_value(conf->stapling_responder,\n                          prev->stapling_responder, \"\");\n \n+    ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0);\n+    ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout,\n+                             1000);\n+    /* Default sizes for the dynamic record sizes are defined to fit maximal\n+       TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi:\n+       1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo,\n+                             1369);\n+    /* 4229 = (1500 - 40 - 20 - 10) * 3  - 61 */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi,\n+                             4229);\n+    ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold,\n+                             40);\n+\n     conf->ssl.log = cf->log;\n \n     if (conf->certificates) {\n@@ -905,6 +959,28 @@\n         return NGX_CONF_ERROR;\n     }\n \n+    if (conf->dyn_rec_enable) {\n+        conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout;\n+        conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold;\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_lo) {\n+            conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_lo = conf->buffer_size;\n+        }\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_hi) {\n+            conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_hi = conf->buffer_size;\n+        }\n+\n+    } else {\n+        conf->ssl.dyn_rec.timeout = 0;\n+    }\n+\n     return NGX_CONF_OK;\n }\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h\n--- a/src/http/modules/ngx_http_ssl_module.h\t2025-04-16 20:01:11.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.h\t2025-04-17 02:43:15.618511766 +0800\n@@ -64,6 +64,12 @@\n     ngx_flag_t                      stapling_verify;\n     ngx_str_t                       stapling_file;\n     ngx_str_t                       stapling_responder;\n+\n+    ngx_flag_t                      dyn_rec_enable;\n+    ngx_msec_t                      dyn_rec_timeout;\n+    size_t                          dyn_rec_size_lo;\n+    size_t                          dyn_rec_size_hi;\n+    ngx_uint_t                      dyn_rec_threshold;\n } ngx_http_ssl_srv_conf_t;\n \n"
  },
  {
    "path": "nginx_for_1.23.4.patch",
    "content": "Add HTTP2 HPACK Encoding Support.\nAdd Dynamic TLS Record Support.\n\nUsing: patch -p1 < nginx_for_1.23.4.patch\n\ndiff --color -uNr a/auto/modules b/auto/modules\n--- a/auto/modules\t2021-11-02 22:49:22.000000000 +0800\n+++ b/auto/modules\t2021-11-04 19:41:20.976743998 +0800\n@@ -423,6 +423,10 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_V2_HPACK_ENC = YES ]; then\n+        have=NGX_HTTP_V2_HPACK_ENC . auto/have\n+    fi\n+\n     if :; then\n         ngx_module_name=ngx_http_static_module\n         ngx_module_incs=\ndiff --color -uNr a/auto/options b/auto/options\n--- a/auto/options\t2021-11-02 22:49:22.000000000 +0800\n+++ b/auto/options\t2021-11-04 19:41:20.977744024 +0800\n@@ -59,6 +59,7 @@\n HTTP_GZIP=YES\n HTTP_SSL=NO\n HTTP_V2=NO\n+HTTP_V2_HPACK_ENC=NO\n HTTP_SSI=YES\n HTTP_REALIP=NO\n HTTP_XSLT=NO\n@@ -227,6 +228,7 @@\n \n         --with-http_ssl_module)          HTTP_SSL=YES               ;;\n         --with-http_v2_module)           HTTP_V2=YES                ;;\n+        --with-http_v2_hpack_enc)        HTTP_V2_HPACK_ENC=YES      ;;\n         --with-http_realip_module)       HTTP_REALIP=YES            ;;\n         --with-http_addition_module)     HTTP_ADDITION=YES          ;;\n         --with-http_xslt_module)         HTTP_XSLT=YES              ;;\n@@ -443,6 +445,7 @@\n \n   --with-http_ssl_module             enable ngx_http_ssl_module\n   --with-http_v2_module              enable ngx_http_v2_module\n+  --with-http_v2_hpack_enc           enable ngx_http_v2_hpack_enc\n   --with-http_realip_module          enable ngx_http_realip_module\n   --with-http_addition_module        enable ngx_http_addition_module\n   --with-http_xslt_module            enable ngx_http_xslt_module\ndiff --color -uNr a/src/core/ngx_murmurhash.c b/src/core/ngx_murmurhash.c\n--- a/src/core/ngx_murmurhash.c\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/core/ngx_murmurhash.c\t2021-11-04 19:41:20.977744024 +0800\n@@ -50,3 +50,63 @@\n \n     return h;\n }\n+\n+\n+uint64_t\n+ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed)\n+{\n+    uint64_t  h, k;\n+\n+    h = seed ^ len;\n+\n+    while (len >= 8) {\n+        k  = data[0];\n+        k |= data[1] << 8;\n+        k |= data[2] << 16;\n+        k |= data[3] << 24;\n+        k |= (uint64_t)data[4] << 32;\n+        k |= (uint64_t)data[5] << 40;\n+        k |= (uint64_t)data[6] << 48;\n+        k |= (uint64_t)data[7] << 56;\n+\n+        k *= 0xc6a4a7935bd1e995ull;\n+        k ^= k >> 47;\n+        k *= 0xc6a4a7935bd1e995ull;\n+\n+        h ^= k;\n+        h *= 0xc6a4a7935bd1e995ull;\n+\n+        data += 8;\n+        len -= 8;\n+    }\n+\n+    switch (len) {\n+    case 7:\n+        h ^= (uint64_t)data[6] << 48;\n+        /* fall through */\n+    case 6:\n+        h ^= (uint64_t)data[5] << 40;\n+        /* fall through */\n+    case 5:\n+        h ^= (uint64_t)data[4] << 32;\n+        /* fall through */\n+    case 4:\n+        h ^= data[3] << 24;\n+        /* fall through */\n+    case 3:\n+        h ^= data[2] << 16;\n+        /* fall through */\n+    case 2:\n+        h ^= data[1] << 8;\n+        /* fall through */\n+    case 1:\n+        h ^= data[0];\n+        h *= 0xc6a4a7935bd1e995ull;\n+    }\n+\n+    h ^= h >> 47;\n+    h *= 0xc6a4a7935bd1e995ull;\n+    h ^= h >> 47;\n+\n+    return h;\n+}\ndiff --color -uNr a/src/core/ngx_murmurhash.h b/src/core/ngx_murmurhash.h\n--- a/src/core/ngx_murmurhash.h\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/core/ngx_murmurhash.h\t2021-11-04 19:41:20.977744024 +0800\n@@ -15,5 +15,7 @@\n \n uint32_t ngx_murmur_hash2(u_char *data, size_t len);\n \n+uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed);\n+\n \n #endif /* _NGX_MURMURHASH_H_INCLUDED_ */\ndiff --color -uNr a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c\n--- a/src/event/ngx_event_openssl.c\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/event/ngx_event_openssl.c\t2021-11-04 19:41:20.979744075 +0800\n@@ -1626,6 +1626,7 @@\n \n     sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);\n     sc->buffer_size = ssl->buffer_size;\n+    sc->dyn_rec = ssl->dyn_rec;\n \n     sc->session_ctx = ssl->ctx;\n \n@@ -2592,6 +2593,41 @@\n \n     for ( ;; ) {\n \n+        /* Dynamic record resizing:\n+           We want the initial records to fit into one TCP segment\n+           so we don't get TCP HoL blocking due to TCP Slow Start.\n+           A connection always starts with small records, but after\n+           a given amount of records sent, we make the records larger\n+           to reduce header overhead.\n+           After a connection has idled for a given timeout, begin\n+           the process from the start. The actual parameters are\n+           configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */\n+\n+        if (c->ssl->dyn_rec.timeout > 0 ) {\n+\n+            if (ngx_current_msec - c->ssl->dyn_rec_last_write >\n+                c->ssl->dyn_rec.timeout)\n+            {\n+                buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                c->ssl->dyn_rec_records_sent = 0;\n+\n+            } else {\n+                if (c->ssl->dyn_rec_records_sent >\n+                    c->ssl->dyn_rec.threshold * 2)\n+                {\n+                    buf->end = buf->start + c->ssl->buffer_size;\n+\n+                } else if (c->ssl->dyn_rec_records_sent >\n+                           c->ssl->dyn_rec.threshold)\n+                {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_hi;\n+\n+                } else {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                }\n+            }\n+        }\n+\n         while (in && buf->last < buf->end && send < limit) {\n             if (in->buf->last_buf || in->buf->flush) {\n                 flush = 1;\n@@ -2731,6 +2767,9 @@\n \n     if (n > 0) {\n \n+        c->ssl->dyn_rec_records_sent++;\n+        c->ssl->dyn_rec_last_write = ngx_current_msec;\n+\n         if (c->ssl->saved_read_handler) {\n \n             c->read->handler = c->ssl->saved_read_handler;\ndiff --color -uNr a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h\n--- a/src/event/ngx_event_openssl.h\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/event/ngx_event_openssl.h\t2021-11-04 19:41:20.979744075 +0800\n@@ -78,10 +78,19 @@\n typedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;\n \n \n+typedef struct {\n+    ngx_msec_t                  timeout;\n+    ngx_uint_t                  threshold;\n+    size_t                      size_lo;\n+    size_t                      size_hi;\n+} ngx_ssl_dyn_rec_t;\n+\n+\n struct ngx_ssl_s {\n     SSL_CTX                    *ctx;\n     ngx_log_t                  *log;\n     size_t                      buffer_size;\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n };\n \n \n@@ -119,6 +128,10 @@\n     unsigned                    in_ocsp:1;\n     unsigned                    early_preread:1;\n     unsigned                    write_blocked:1;\n+\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n+    ngx_msec_t                  dyn_rec_last_write;\n+    ngx_uint_t                  dyn_rec_records_sent;\n };\n \n \n@@ -128,7 +141,7 @@\n #define NGX_SSL_DFLT_BUILTIN_SCACHE  -5\n \n \n-#define NGX_SSL_MAX_SESSION_SIZE  4096\n+#define NGX_SSL_MAX_SESSION_SIZE  16384\n \n typedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c\n--- a/src/http/modules/ngx_http_ssl_module.c\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.c\t2021-11-04 19:41:20.980744101 +0800\n@@ -296,6 +296,41 @@\n       offsetof(ngx_http_ssl_srv_conf_t, reject_handshake),\n       NULL },\n \n+    { ngx_string(\"ssl_dyn_rec_enable\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_flag_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_lo\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_hi\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_threshold\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold),\n+      NULL },\n+\n       ngx_null_command\n };\n \n@@ -595,6 +630,11 @@\n     sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR;\n     sscf->stapling = NGX_CONF_UNSET;\n     sscf->stapling_verify = NGX_CONF_UNSET;\n+    sscf->dyn_rec_enable = NGX_CONF_UNSET;\n+    sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC;\n+    sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT;\n \n     return sscf;\n }\n@@ -670,6 +710,20 @@\n     ngx_conf_merge_str_value(conf->stapling_responder,\n                          prev->stapling_responder, \"\");\n \n+    ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0);\n+    ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout,\n+                             1000);\n+    /* Default sizes for the dynamic record sizes are defined to fit maximal\n+       TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi:\n+       1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo,\n+                             1369);\n+    /* 4229 = (1500 - 40 - 20 - 10) * 3  - 61 */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi,\n+                             4229);\n+    ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold,\n+                             40);\n+\n     conf->ssl.log = cf->log;\n \n     if (conf->enable) {\n@@ -896,6 +950,28 @@\n         return NGX_CONF_ERROR;\n     }\n \n+    if (conf->dyn_rec_enable) {\n+        conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout;\n+        conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold;\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_lo) {\n+            conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_lo = conf->buffer_size;\n+        }\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_hi) {\n+            conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_hi = conf->buffer_size;\n+        }\n+\n+    } else {\n+        conf->ssl.dyn_rec.timeout = 0;\n+    }\n+\n     return NGX_CONF_OK;\n }\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h\n--- a/src/http/modules/ngx_http_ssl_module.h\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.h\t2021-11-04 19:41:20.981744126 +0800\n@@ -67,6 +67,12 @@\n \n     u_char                         *file;\n     ngx_uint_t                      line;\n+\n+    ngx_flag_t                      dyn_rec_enable;\n+    ngx_msec_t                      dyn_rec_timeout;\n+    size_t                          dyn_rec_size_lo;\n+    size_t                          dyn_rec_size_hi;\n+    ngx_uint_t                      dyn_rec_threshold;\n } ngx_http_ssl_srv_conf_t;\n \n \ndiff --color -uNr a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c\n--- a/src/http/v2/ngx_http_v2.c\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.c\t2021-11-04 19:41:20.982744152 +0800\n@@ -274,6 +274,8 @@\n \n     h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n \n+    h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE;\n+\n     h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n \n     h2c->concurrent_pushes = h2scf->concurrent_pushes;\n@@ -2283,6 +2285,14 @@\n         case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:\n \n             h2c->table_update = 1;\n+\n+            if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+                h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE;\n+            } else {\n+                h2c->max_hpack_table_size = value;\n+            }\n+\n+            h2c->indicate_resize = 1;\n             break;\n \n         default:\ndiff --color -uNr a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c\n--- a/src/http/v2/ngx_http_v2_encode.c\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_encode.c\t2021-11-04 19:41:20.983744177 +0800\n@@ -10,7 +10,7 @@\n #include <ngx_http.h>\n \n \n-static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n+u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n     ngx_uint_t value);\n \n \n@@ -40,7 +40,7 @@\n }\n \n \n-static u_char *\n+u_char *\n ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)\n {\n     if (value < prefix) {\ndiff --color -uNr a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c\n--- a/src/http/v2/ngx_http_v2_filter_module.c\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_filter_module.c\t2021-11-04 19:41:20.984744203 +0800\n@@ -23,10 +23,53 @@\n #define ngx_http_v2_literal_size(h)                                           \\\n     (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)\n \n+#define ngx_http_v2_indexed(i)      (128 + (i))\n+#define ngx_http_v2_inc_indexed(i)  (64 + (i))\n+\n+#define NGX_HTTP_V2_ENCODE_RAW            0\n+#define NGX_HTTP_V2_ENCODE_HUFF           0x80\n+\n+#define NGX_HTTP_V2_AUTHORITY_INDEX       1\n+#define NGX_HTTP_V2_METHOD_GET_INDEX      2\n+#define NGX_HTTP_V2_PATH_INDEX            4\n+\n+#define NGX_HTTP_V2_SCHEME_HTTP_INDEX     6\n+#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX    7\n+\n+#define NGX_HTTP_V2_STATUS_INDEX          8\n+#define NGX_HTTP_V2_STATUS_200_INDEX      8\n+#define NGX_HTTP_V2_STATUS_204_INDEX      9\n+#define NGX_HTTP_V2_STATUS_206_INDEX      10\n+#define NGX_HTTP_V2_STATUS_304_INDEX      11\n+#define NGX_HTTP_V2_STATUS_400_INDEX      12\n+#define NGX_HTTP_V2_STATUS_404_INDEX      13\n+#define NGX_HTTP_V2_STATUS_500_INDEX      14\n+\n+#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16\n+#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17\n+#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX  28\n+#define NGX_HTTP_V2_CONTENT_TYPE_INDEX    31\n+#define NGX_HTTP_V2_DATE_INDEX            33\n+#define NGX_HTTP_V2_LAST_MODIFIED_INDEX   44\n+#define NGX_HTTP_V2_LOCATION_INDEX        46\n+#define NGX_HTTP_V2_SERVER_INDEX          54\n+#define NGX_HTTP_V2_USER_AGENT_INDEX      58\n+#define NGX_HTTP_V2_VARY_INDEX            59\n \n #define NGX_HTTP_V2_NO_TRAILERS           (ngx_http_v2_out_frame_t *) -1\n \n \n+static const struct {\n+    u_char        *name;\n+    u_char const   len;\n+} push_header[] = {\n+    { (u_char*)\":authority\"      , 10 },\n+    { (u_char*)\"accept-encoding\" , 15 },\n+    { (u_char*)\"accept-language\" , 15 },\n+    { (u_char*)\"user-agent\"      , 10 }\n+};\n+\n+\n typedef struct {\n     ngx_str_t      name;\n     u_char         index;\n@@ -155,11 +198,9 @@\n #endif\n \n     static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);\n-    static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];\n \n     static size_t nginx_ver_build_len =\n                                   ngx_http_v2_literal_size(NGINX_VER_BUILD);\n-    static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];\n \n     stream = r->stream;\n \n@@ -435,7 +476,7 @@\n     }\n \n     tmp = ngx_palloc(r->pool, tmp_len);\n-    pos = ngx_pnalloc(r->pool, len);\n+    pos = ngx_pnalloc(r->pool, len + 15 + 1);\n \n     if (pos == NULL || tmp == NULL) {\n         return NGX_ERROR;\n@@ -443,11 +484,16 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n     }\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n@@ -458,67 +504,28 @@\n         *pos++ = status;\n \n     } else {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);\n-        *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;\n-        pos = ngx_sprintf(pos, \"%03ui\", r->headers_out.status);\n+        ngx_sprintf(pos + 8, \"%O3ui\", r->headers_out.status);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\":status\",\n+                                       sizeof(\":status\") - 1, pos + 8, 3, tmp);\n     }\n \n     if (r->headers_out.server == NULL) {\n-\n         if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER);\n-\n-        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER_BUILD);\n-\n-        } else {\n-            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: nginx\\\"\");\n-        }\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);\n-\n-        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            if (nginx_ver[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,\n-                                            sizeof(NGINX_VER) - 1, tmp);\n-                nginx_ver_len = p - nginx_ver;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER);\n \n         } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            if (nginx_ver_build[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver_build,\n-                                            (u_char *) NGINX_VER_BUILD,\n-                                            sizeof(NGINX_VER_BUILD) - 1, tmp);\n-                nginx_ver_build_len = p - nginx_ver_build;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER_BUILD);\n \n         } else {\n-            pos = ngx_cpymem(pos, nginx, sizeof(nginx));\n+            pos = ngx_http_v2_write_header_str(\"server\", \"nginx\");\n         }\n     }\n \n     if (r->headers_out.date == NULL) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"date: %V\\\"\",\n-                       &ngx_cached_http_time);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);\n-        pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,\n-                                      ngx_cached_http_time.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"date\", ngx_cached_http_time);\n     }\n \n     if (r->headers_out.content_type.len) {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);\n-\n         if (r->headers_out.content_type_len == r->headers_out.content_type.len\n             && r->headers_out.charset.len)\n         {\n@@ -544,64 +551,36 @@\n             r->headers_out.content_type.data = p - len;\n         }\n \n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-type: %V\\\"\",\n-                       &r->headers_out.content_type);\n-\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,\n-                                      r->headers_out.content_type.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"content-type\",\n+                                           r->headers_out.content_type);\n     }\n \n     if (r->headers_out.content_length == NULL\n         && r->headers_out.content_length_n >= 0)\n     {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-length: %O\\\"\",\n-                       r->headers_out.content_length_n);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);\n-\n-        p = pos;\n-        pos = ngx_sprintf(pos + 1, \"%O\", r->headers_out.content_length_n);\n-        *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);\n+        p = ngx_sprintf(pos + 15, \"%O\", r->headers_out.content_length_n);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"content-length\",\n+                                       sizeof(\"content-length\") - 1, pos + 15,\n+                                       p - (pos + 15), tmp);\n     }\n \n     if (r->headers_out.last_modified == NULL\n         && r->headers_out.last_modified_time != -1)\n     {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);\n-\n-        ngx_http_time(pos, r->headers_out.last_modified_time);\n+        ngx_http_time(pos + 14, r->headers_out.last_modified_time);\n         len = sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1;\n-\n-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"last-modified: %*s\\\"\",\n-                       len, pos);\n-\n-        /*\n-         * Date will always be encoded using huffman in the temporary buffer,\n-         * so it's safe here to use src and dst pointing to the same address.\n-         */\n-        pos = ngx_http_v2_write_value(pos, pos, len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"last-modified\",\n+                                       sizeof(\"last-modified\") - 1, pos + 14,\n+                                       len, tmp);\n     }\n \n     if (r->headers_out.location && r->headers_out.location->value.len) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"location: %V\\\"\",\n-                       &r->headers_out.location->value);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,\n-                                      r->headers_out.location->value.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"location\", r->headers_out.location->value);\n     }\n \n #if (NGX_HTTP_GZIP)\n     if (r->gzip_vary) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"vary: Accept-Encoding\\\"\");\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);\n-        pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));\n+        pos = ngx_http_v2_write_header_str(\"vary\", \"Accept-Encoding\");\n     }\n #endif\n \n@@ -624,23 +603,10 @@\n             continue;\n         }\n \n-#if (NGX_DEBUG)\n-        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n-            ngx_strlow(tmp, header[i].key.data, header[i].key.len);\n-\n-            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"%*s: %V\\\"\",\n-                           header[i].key.len, tmp, &header[i].value);\n-        }\n-#endif\n-\n-        *pos++ = 0;\n-\n-        pos = ngx_http_v2_write_name(pos, header[i].key.data,\n-                                     header[i].key.len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data,\n+                                       header[i].key.len, header[i].value.data,\n+                                       header[i].value.len, tmp);\n \n-        pos = ngx_http_v2_write_value(pos, header[i].value.data,\n-                                      header[i].value.len, tmp);\n     }\n \n     fin = r->header_only\n@@ -998,6 +964,7 @@\n \n     for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n         len += binary[i].len;\n+        len += push_header[i].len + 1;\n     }\n \n     pos = ngx_pnalloc(r->pool, len);\n@@ -1007,12 +974,17 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n-    }\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n+     }\n \n     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":method: GET\\\"\");\n@@ -1022,8 +994,7 @@\n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":path: %V\\\"\", path);\n \n-    *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n-    pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);\n+    pos = ngx_http_v2_write_header_pot(\":path\", path);\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":scheme: %V\\\"\", &r->schema);\n@@ -1048,11 +1019,15 @@\n             continue;\n         }\n \n+        value = &(*h)->value;\n+\n         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                        \"http2 push header: \\\"%V: %V\\\"\",\n                        &ph[i].name, &(*h)->value);\n \n-        pos = ngx_cpymem(pos, binary[i].data, binary[i].len);\n+        pos = ngx_http_v2_write_header(h2c, pos,\n+                  push_header[i].name, push_header[i].len, value->data, value->len,\n+                  tmp);\n     }\n \n     frame = ngx_http_v2_create_push_frame(r, start, pos);\ndiff --color -uNr a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h\n--- a/src/http/v2/ngx_http_v2.h\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.h\t2021-11-04 19:41:20.985744228 +0800\n@@ -51,6 +51,14 @@\n #define NGX_HTTP_V2_MAX_WINDOW           ((1U << 31) - 1)\n #define NGX_HTTP_V2_DEFAULT_WINDOW       65535\n \n+#define HPACK_ENC_HTABLE_SZ              128 /* better to keep a PoT < 64k */\n+#define HPACK_ENC_HTABLE_ENTRIES         ((HPACK_ENC_HTABLE_SZ * 100) / 128)\n+#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ     10  /* 10 is sufficient for most */\n+#define HPACK_ENC_MAX_ENTRY              512 /* longest header size to match */\n+\n+#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE     4096\n+#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE         16384 /* < 64k */\n+\n #define NGX_HTTP_V2_DEFAULT_WEIGHT       16\n \n \n@@ -114,6 +122,46 @@\n } ngx_http_v2_hpack_t;\n \n \n+#if (NGX_HTTP_V2_HPACK_ENC)\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen, vlen;\n+    uint16_t                         size;\n+    uint16_t                         next;\n+} ngx_http_v2_hpack_enc_entry_t;\n+\n+\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen;\n+} ngx_http_v2_hpack_name_entry_t;\n+\n+\n+typedef struct {\n+    size_t                           size;    /* size as defined in RFC 7541 */\n+    uint32_t                         top;     /* the last entry */\n+    uint32_t                         pos;\n+    uint16_t                         n_elems; /* number of elements */\n+    uint16_t                         base;    /* index of the oldest entry */\n+    uint16_t                         last;    /* index of the newest entry */\n+\n+    /* hash table for dynamic entries, instead using a generic hash table,\n+       which would be too slow to process a significant amount of headers,\n+       this table is not determenistic, and might ocasionally fail to insert\n+       a value, at the cost of slightly worse compression, but significantly\n+       faster performance */\n+    ngx_http_v2_hpack_enc_entry_t    htable[HPACK_ENC_HTABLE_SZ];\n+    ngx_http_v2_hpack_name_entry_t   heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ];\n+    u_char                           storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE +\n+                                             HPACK_ENC_MAX_ENTRY];\n+} ngx_http_v2_hpack_enc_t;\n+#endif\n+\n+\n struct ngx_http_v2_connection_s {\n     ngx_connection_t                *connection;\n     ngx_http_connection_t           *http_connection;\n@@ -135,6 +183,8 @@\n \n     size_t                           frame_size;\n \n+    size_t                           max_hpack_table_size;\n+\n     ngx_queue_t                      waiting;\n \n     ngx_http_v2_state_t              state;\n@@ -164,6 +214,11 @@\n     unsigned                         blocked:1;\n     unsigned                         goaway:1;\n     unsigned                         push_disabled:1;\n+    unsigned                         indicate_resize:1;\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+    ngx_http_v2_hpack_enc_t          hpack_enc;\n+#endif\n };\n \n \n@@ -207,6 +262,8 @@\n \n     ngx_array_t                     *cookies;\n \n+    size_t                           header_limit;\n+\n     ngx_pool_t                      *pool;\n \n     unsigned                         waiting:1;\n@@ -419,4 +476,35 @@\n     u_char *tmp, ngx_uint_t lower);\n \n \n+u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,\n+    u_char *tmp, ngx_uint_t lower);\n+\n+u_char *\n+ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value);\n+\n+#define ngx_http_v2_write_name(dst, src, len, tmp)                            \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 1)\n+#define ngx_http_v2_write_value(dst, src, len, tmp)                           \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 0)\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+    u_char *key, size_t key_len, u_char *value, size_t value_len,\n+    u_char *tmp);\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c);\n+\n+#define ngx_http_v2_write_header_str(key, value)                        \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    (u_char *) value, sizeof(value) - 1, tmp);\n+\n+#define ngx_http_v2_write_header_tbl(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val.data, val.len, tmp);\n+\n+#define ngx_http_v2_write_header_pot(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val->data, val->len, tmp);\n+\n #endif /* _NGX_HTTP_V2_H_INCLUDED_ */\ndiff --color -uNr a/src/http/v2/ngx_http_v2_table.c b/src/http/v2/ngx_http_v2_table.c\n--- a/src/http/v2/ngx_http_v2_table.c\t2021-11-02 22:49:22.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_table.c\t2021-11-04 19:41:20.986744254 +0800\n@@ -361,3 +361,434 @@\n \n     return NGX_OK;\n }\n+\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len);\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len);\n+\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c)\n+{\n+    ngx_http_v2_hpack_enc_entry_t  *table;\n+    uint64_t                        idx;\n+\n+    table = h2c->hpack_enc.htable;\n+\n+    while (h2c->hpack_enc.size > h2c->max_hpack_table_size) {\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+}\n+\n+\n+/* checks if a header is in the hpack table - if so returns the table entry,\n+   otherwise encodes and inserts into the table and returns 0,\n+   if failed to insert into table, returns -1 */\n+static ngx_int_t\n+ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c,\n+    size_t key_len, size_t val_len, uint8_t *key, uint8_t *val,\n+    ngx_int_t *header_idx)\n+{\n+    uint64_t  hash_val, key_hash, idx, lru;\n+    int       i;\n+    size_t    size = key_len + val_len + 32;\n+    uint8_t  *storage = h2c->hpack_enc.storage;\n+\n+    ngx_http_v2_hpack_enc_entry_t   *table;\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+\n+    *header_idx = NGX_ERROR;\n+    /* step 1: compute the hash value of header */\n+    if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) {\n+        return NGX_ERROR;\n+    }\n+\n+    key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234);\n+    hash_val = ngx_murmur_hash2_64(val, val_len, key_hash);\n+\n+    if (hash_val == 0) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* step 2: check if full header in the table */\n+    idx = hash_val;\n+    i = -1;\n+    while (idx) {\n+         /* at most 8 locations are checked, but most will be done in 1 or 2 */\n+        table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ];\n+        if (table->hash_val == hash_val\n+            && table->klen == key_len\n+            && table->vlen == val_len\n+            && ngx_memcmp(key, storage + table->pos, key_len) == 0\n+            && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0)\n+        {\n+            return (h2c->hpack_enc.top - table->index) + 61;\n+        }\n+\n+        if (table->hash_val == 0 && i == -1) {\n+            i = idx % HPACK_ENC_HTABLE_SZ;\n+            break;\n+        }\n+\n+        idx >>= 8;\n+    }\n+\n+    /* step 3: check if key is in one of the tables */\n+    *header_idx = hpack_get_static_index(h2c, key, key_len);\n+\n+    if (i == -1) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (*header_idx == NGX_ERROR) {\n+        *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len);\n+    }\n+\n+    /* step 4: store the new entry */\n+    table =  h2c->hpack_enc.htable;\n+\n+    if (h2c->hpack_enc.top == 0xffffffff) {\n+        /* just to be on the safe side, avoid overflow */\n+        ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t));\n+    }\n+\n+    while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size)\n+           || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) {\n+        /* make space for the new entry first */\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+\n+    table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val,\n+                                               .index = h2c->hpack_enc.top,\n+                                               .pos = h2c->hpack_enc.pos,\n+                                               .klen = key_len,\n+                                               .vlen = val_len,\n+                                               .size = size,\n+                                               .next = 0};\n+\n+    table[h2c->hpack_enc.last].next = i;\n+    if (h2c->hpack_enc.n_elems == 0) {\n+        h2c->hpack_enc.base = i;\n+    }\n+\n+    h2c->hpack_enc.last = i;\n+    h2c->hpack_enc.top++;\n+    h2c->hpack_enc.size += size;\n+    h2c->hpack_enc.n_elems++;\n+\n+    /* update header name lookup */\n+    if (*header_idx == NGX_ERROR ) {\n+        lru = h2c->hpack_enc.top;\n+\n+        for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+\n+            name = &h2c->hpack_enc.heads[i];\n+\n+            if ( name->hash_val == 0 || (name->hash_val == key_hash\n+                && ngx_memcmp(storage + name->pos, key, key_len) == 0) )\n+            {\n+                name->hash_val = key_hash;\n+                name->pos = h2c->hpack_enc.pos;\n+                name->index = h2c->hpack_enc.top - 1;\n+                break;\n+            }\n+\n+            if (lru > name->index) {\n+                lru = name->index;\n+                idx = i;\n+            }\n+        }\n+\n+        if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) {\n+            name = &h2c->hpack_enc.heads[idx];\n+            name->hash_val = hash_val;\n+            name->pos = h2c->hpack_enc.pos;\n+            name->index = h2c->hpack_enc.top - 1;\n+        }\n+    }\n+\n+    ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len);\n+    ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len);\n+\n+    h2c->hpack_enc.pos += size;\n+    if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+        h2c->hpack_enc.pos = 0;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_int_t idx, header_idx;\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    /* attempt to find the value in the dynamic table */\n+    idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value,\n+                                           &header_idx);\n+\n+    if (idx > 0) {\n+        /* positive index indicates success */\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                       \"http2 hpack encode: Indexed Header Field: %ud\", idx);\n+\n+        *pos = 128;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx);\n+\n+    } else {\n+\n+        if (header_idx == NGX_ERROR) { /* if key is not present */\n+\n+            if (idx == NGX_ERROR) {    /* if header was not added */\n+                *pos++ = 0;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — New Name\");\n+            } else {                   /* if header was added */\n+                *pos++ = 64;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — New Name\");\n+            }\n+\n+            pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+\n+        } else {                       /* if key is present */\n+\n+            if (idx == NGX_ERROR) {\n+                *pos = 0;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — Indexed Name: %ud\", header_idx);\n+            } else {\n+                *pos = 64;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — Indexed Name: %ud\", header_idx);\n+            }\n+        }\n+\n+        pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+    }\n+\n+    return pos;\n+}\n+\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len)\n+{\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+    int                              i;\n+\n+    for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+        name = &h2c->hpack_enc.heads[i];\n+\n+        if (name->hash_val == key_hash\n+            && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0)\n+        {\n+            if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) {\n+                return (h2c->hpack_enc.top - name->index) + 61;\n+            }\n+            break;\n+        }\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+\n+/* decide if a given header is present in the static dictionary, this could be\n+   done in several ways, but it seems the fastest one is \"exhaustive\" search */\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len)\n+{\n+    /* the static dictionary of response only headers,\n+       although response headers can be put by origin,\n+       that would be rare */\n+    static const struct {\n+        u_char         len;\n+        const u_char   val[28];\n+        u_char         idx;\n+    } server_headers[] = {\n+        { 3, \"age\",                         21},//0\n+        { 3, \"via\",                         60},\n+        { 4, \"date\",                        33},//2\n+        { 4, \"etag\",                        34},\n+        { 4, \"link\",                        45},\n+        { 4, \"vary\",                        59},\n+        { 5, \"allow\",                       22},//6\n+        { 6, \"server\",                      54},//7\n+        { 7, \"expires\",                     36},//8\n+        { 7, \"refresh\",                     52},\n+        { 8, \"location\",                    46},//10\n+        {10, \"set-cookie\",                  55},//11\n+        {11, \"retry-after\",                 53},//12\n+        {12, \"content-type\",                31},//13\n+        {13, \"content-range\",               30},//14\n+        {13, \"accept-ranges\",               18},\n+        {13, \"cache-control\",               24},\n+        {13, \"last-modified\",               44},\n+        {14, \"content-length\",              28},//18\n+        {16, \"content-encoding\",            26},//19\n+        {16, \"content-language\",            27},\n+        {16, \"content-location\",            29},\n+        {16, \"www-authenticate\",            61},\n+        {17, \"transfer-encoding\",           57},//23\n+        {18, \"proxy-authenticate\",          48},//24\n+        {19, \"content-disposition\",         25},//25\n+        {25, \"strict-transport-security\",   56},//26\n+        {27, \"access-control-allow-origin\", 20},//27\n+        {99, \"\",                            99},\n+    }, *header;\n+\n+    /* for a given length, where to start the search\n+       since minimal length is 3, the table has a -3\n+       offset */\n+    static const int8_t start_at[] = {\n+        [3-3]  = 0,\n+        [4-3]  = 2,\n+        [5-3]  = 6,\n+        [6-3]  = 7,\n+        [7-3]  = 8,\n+        [8-3]  = 10,\n+        [9-3]  = -1,\n+        [10-3] = 11,\n+        [11-3] = 12,\n+        [12-3] = 13,\n+        [13-3] = 14,\n+        [14-3] = 18,\n+        [15-3] = -1,\n+        [16-3] = 19,\n+        [17-3] = 23,\n+        [18-3] = 24,\n+        [19-3] = 25,\n+        [20-3] = -1,\n+        [21-3] = -1,\n+        [22-3] = -1,\n+        [23-3] = -1,\n+        [24-3] = -1,\n+        [25-3] = 26,\n+        [26-3] = -1,\n+        [27-3] = 27,\n+    };\n+\n+    uint64_t pref;\n+    size_t   save_len = len, i;\n+    int8_t   start;\n+\n+    /* early exit for out of bounds lengths */\n+    if (len < 3 || len > 27) {\n+        return NGX_ERROR;\n+    }\n+\n+    start = start_at[len - 3];\n+    if (start == -1) {\n+        /* exit for non existent lengths */\n+        return NGX_ERROR;\n+    }\n+\n+    header = &server_headers[start_at[len - 3]];\n+\n+    /* load first 8 bytes of key, for fast comparison */\n+    if (len < 8) {\n+        pref = 0;\n+        if (len >= 4) {\n+            pref = *(uint32_t *)(val + len - 4) | 0x20202020;\n+            len -= 4;\n+        }\n+        while (len > 0) { /* 3 iterations at most */\n+            pref = (pref << 8) ^ (val[len - 1] | 0x20);\n+            len--;\n+        }\n+    } else {\n+        pref = *(uint64_t *)val | 0x2020202020202020;\n+        len -= 8;\n+    }\n+\n+    /* iterate over headers with the right length */\n+    while (header->len == save_len) {\n+        /* quickly compare the first 8 bytes, most tests will end here */\n+        if (pref != *(uint64_t *) header->val) {\n+            header++;\n+            continue;\n+        }\n+\n+        if (len == 0) {\n+            /* len == 0, indicates prefix held the entire key */\n+            return header->idx;\n+        }\n+        /* for longer keys compare the rest */\n+        i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */\n+\n+        while (i + 8 <= save_len) { /* 3 iterations at most */\n+            if ( *(uint64_t *)&header->val[i]\n+                 != (*(uint64_t *) &val[i]| 0x2020202020202020) )\n+            {\n+                header++;\n+                i = 0;\n+                break;\n+            }\n+            i += 8;\n+        }\n+\n+        if (i == 0) {\n+            continue;\n+        }\n+\n+        /* found the corresponding entry in the static dictionary */\n+        return header->idx;\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+#else\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    *pos++ = 64;\n+    pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+    pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+\n+    return pos;\n+}\n+\n+#endif\n"
  },
  {
    "path": "nginx_io_uring.patch",
    "content": "From a052b2d1adf583233454a6d7f89963544714ff58 Mon Sep 17 00:00:00 2001\nFrom: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= <carter.li@eoitek.com>\nDate: Fri, 14 Aug 2020 10:44:44 +0800\nSubject: [PATCH] Add io_uring support\n\n---\n auto/unix                            |  39 ++---\n src/core/ngx_open_file_cache.c       |   6 +-\n src/event/modules/ngx_epoll_module.c | 225 ++++++---------------------\n src/event/ngx_event.h                |   4 +-\n src/os/unix/ngx_linux_aio_read.c     |  63 +++++---\n src/os/unix/ngx_linux_config.h       |   4 -\n 6 files changed, 98 insertions(+), 243 deletions(-)\n\ndiff --git a/auto/unix b/auto/unix\nindex ff9697a..2e2f63a 100644\n--- a/auto/unix\n+++ b/auto/unix\n@@ -532,44 +532,23 @@ if [ $NGX_FILE_AIO = YES ]; then\n \n     if [ $ngx_found = no ]; then\n \n-        ngx_feature=\"Linux AIO support\"\n+        ngx_feature=\"Linux io_uring support (liburing)\"\n         ngx_feature_name=\"NGX_HAVE_FILE_AIO\"\n         ngx_feature_run=no\n-        ngx_feature_incs=\"#include <linux/aio_abi.h>\n-                          #include <sys/eventfd.h>\"\n+        ngx_feature_incs=\"#include <liburing.h>\"\n         ngx_feature_path=\n-        ngx_feature_libs=\n-        ngx_feature_test=\"struct iocb  iocb;\n-                          iocb.aio_lio_opcode = IOCB_CMD_PREAD;\n-                          iocb.aio_flags = IOCB_FLAG_RESFD;\n-                          iocb.aio_resfd = -1;\n-                          (void) iocb;\n-                          (void) eventfd(0, 0)\"\n+        ngx_feature_libs=\"-luring\"\n+        ngx_feature_test=\"struct io_uring  ring;\n+                          int ret = io_uring_queue_init(64, &ring, 0);\n+                          if (ret < 0) return 1;\n+                          io_uring_queue_exit(&ring);\"\n         . auto/feature\n \n         if [ $ngx_found = yes ]; then\n             have=NGX_HAVE_EVENTFD . auto/have\n             have=NGX_HAVE_SYS_EVENTFD_H . auto/have\n             CORE_SRCS=\"$CORE_SRCS $LINUX_AIO_SRCS\"\n-        fi\n-    fi\n-\n-    if [ $ngx_found = no ]; then\n-\n-        ngx_feature=\"Linux AIO support (SYS_eventfd)\"\n-        ngx_feature_incs=\"#include <linux/aio_abi.h>\n-                          #include <sys/syscall.h>\"\n-        ngx_feature_test=\"struct iocb  iocb;\n-                          iocb.aio_lio_opcode = IOCB_CMD_PREAD;\n-                          iocb.aio_flags = IOCB_FLAG_RESFD;\n-                          iocb.aio_resfd = -1;\n-                          (void) iocb;\n-                          (void) SYS_eventfd\"\n-        . auto/feature\n-\n-        if [ $ngx_found = yes ]; then\n-            have=NGX_HAVE_EVENTFD . auto/have\n-            CORE_SRCS=\"$CORE_SRCS $LINUX_AIO_SRCS\"\n+            CORE_LIBS=\"$CORE_LIBS -luring\"\n         fi\n     fi\n \n@@ -577,7 +556,7 @@ if [ $NGX_FILE_AIO = YES ]; then\n         cat << END\n \n $0: no supported file AIO was found\n-Currently file AIO is supported on FreeBSD 4.3+ and Linux 2.6.22+ only\n+Currently file AIO is supported on FreeBSD 4.3+ and Linux 5.1.0+ (requires liburing) only\n \n END\n         exit 1\ndiff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c\nindex b23ee78..682d48c 100644\n--- a/src/core/ngx_open_file_cache.c\n+++ b/src/core/ngx_open_file_cache.c\n@@ -869,11 +869,11 @@ ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of,\n     if (!of->log) {\n \n         /*\n-         * Use non-blocking open() not to hang on FIFO files, etc.\n-         * This flag has no effect on a regular files.\n+         * Differs from plain read, IORING_OP_READV with O_NONBLOCK\n+         * will return -EAGAIN if the operation may block.\n          */\n \n-        fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,\n+        fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY,\n                                    NGX_FILE_OPEN, 0, log);\n \n     } else {\ndiff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c\nindex 98e3ce7..09dda1d 100644\n--- a/src/event/modules/ngx_epoll_module.c\n+++ b/src/event/modules/ngx_epoll_module.c\n@@ -9,6 +9,10 @@\n #include <ngx_core.h>\n #include <ngx_event.h>\n \n+#if (NGX_HAVE_FILE_AIO)\n+#include <liburing.h>\n+#endif\n+\n \n #if (NGX_TEST_BUILD_EPOLL)\n \n@@ -75,23 +79,6 @@ int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout)\n #define SYS_eventfd       323\n #endif\n \n-#if (NGX_HAVE_FILE_AIO)\n-\n-#define SYS_io_setup      245\n-#define SYS_io_destroy    246\n-#define SYS_io_getevents  247\n-\n-typedef u_int  aio_context_t;\n-\n-struct io_event {\n-    uint64_t  data;  /* the data field from the iocb */\n-    uint64_t  obj;   /* what iocb this event came from */\n-    int64_t   res;   /* result code for this event */\n-    int64_t   res2;  /* secondary result */\n-};\n-\n-\n-#endif\n #endif /* NGX_TEST_BUILD_EPOLL */\n \n \n@@ -124,7 +111,7 @@ static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n     ngx_uint_t flags);\n \n #if (NGX_HAVE_FILE_AIO)\n-static void ngx_epoll_eventfd_handler(ngx_event_t *ev);\n+static void ngx_epoll_io_uring_handler(ngx_event_t *ev);\n #endif\n \n static void *ngx_epoll_create_conf(ngx_cycle_t *cycle);\n@@ -141,13 +128,11 @@ static ngx_connection_t     notify_conn;\n #endif\n \n #if (NGX_HAVE_FILE_AIO)\n+struct io_uring             ngx_ring;\n+struct io_uring_params      ngx_ring_params;\n \n-int                         ngx_eventfd = -1;\n-aio_context_t               ngx_aio_ctx = 0;\n-\n-static ngx_event_t          ngx_eventfd_event;\n-static ngx_connection_t     ngx_eventfd_conn;\n-\n+static ngx_event_t          ngx_ring_event;\n+static ngx_connection_t     ngx_ring_conn;\n #endif\n \n #if (NGX_HAVE_EPOLLRDHUP)\n@@ -217,102 +202,40 @@ ngx_module_t  ngx_epoll_module = {\n \n #if (NGX_HAVE_FILE_AIO)\n \n-/*\n- * We call io_setup(), io_destroy() io_submit(), and io_getevents() directly\n- * as syscalls instead of libaio usage, because the library header file\n- * supports eventfd() since 0.3.107 version only.\n- */\n-\n-static int\n-io_setup(u_int nr_reqs, aio_context_t *ctx)\n-{\n-    return syscall(SYS_io_setup, nr_reqs, ctx);\n-}\n-\n-\n-static int\n-io_destroy(aio_context_t ctx)\n-{\n-    return syscall(SYS_io_destroy, ctx);\n-}\n-\n-\n-static int\n-io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events,\n-    struct timespec *tmo)\n-{\n-    return syscall(SYS_io_getevents, ctx, min_nr, nr, events, tmo);\n-}\n-\n-\n static void\n ngx_epoll_aio_init(ngx_cycle_t *cycle, ngx_epoll_conf_t *epcf)\n {\n-    int                 n;\n     struct epoll_event  ee;\n \n-#if (NGX_HAVE_SYS_EVENTFD_H)\n-    ngx_eventfd = eventfd(0, 0);\n-#else\n-    ngx_eventfd = syscall(SYS_eventfd, 0);\n-#endif\n-\n-    if (ngx_eventfd == -1) {\n-        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n-                      \"eventfd() failed\");\n-        ngx_file_aio = 0;\n-        return;\n-    }\n-\n-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n-                   \"eventfd: %d\", ngx_eventfd);\n-\n-    n = 1;\n-\n-    if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) {\n-        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n-                      \"ioctl(eventfd, FIONBIO) failed\");\n-        goto failed;\n-    }\n-\n-    if (io_setup(epcf->aio_requests, &ngx_aio_ctx) == -1) {\n+    if (io_uring_queue_init_params(64, &ngx_ring, &ngx_ring_params) < 0) {\n         ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n-                      \"io_setup() failed\");\n+                      \"io_uring_queue_init_params() failed\");\n         goto failed;\n     }\n \n-    ngx_eventfd_event.data = &ngx_eventfd_conn;\n-    ngx_eventfd_event.handler = ngx_epoll_eventfd_handler;\n-    ngx_eventfd_event.log = cycle->log;\n-    ngx_eventfd_event.active = 1;\n-    ngx_eventfd_conn.fd = ngx_eventfd;\n-    ngx_eventfd_conn.read = &ngx_eventfd_event;\n-    ngx_eventfd_conn.log = cycle->log;\n+    ngx_ring_event.data = &ngx_ring_conn;\n+    ngx_ring_event.handler = ngx_epoll_io_uring_handler;\n+    ngx_ring_event.log = cycle->log;\n+    ngx_ring_event.active = 1;\n+    ngx_ring_conn.fd = ngx_ring.ring_fd;\n+    ngx_ring_conn.read = &ngx_ring_event;\n+    ngx_ring_conn.log = cycle->log;\n \n     ee.events = EPOLLIN|EPOLLET;\n-    ee.data.ptr = &ngx_eventfd_conn;\n+    ee.data.ptr = &ngx_ring_conn;\n \n-    if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) != -1) {\n+    if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_ring.ring_fd, &ee) != -1) {\n         return;\n     }\n \n     ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                   \"epoll_ctl(EPOLL_CTL_ADD, eventfd) failed\");\n \n-    if (io_destroy(ngx_aio_ctx) == -1) {\n-        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n-                      \"io_destroy() failed\");\n-    }\n+    io_uring_queue_exit(&ngx_ring);\n \n failed:\n \n-    if (close(ngx_eventfd) == -1) {\n-        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n-                      \"eventfd close() failed\");\n-    }\n-\n-    ngx_eventfd = -1;\n-    ngx_aio_ctx = 0;\n+    ngx_ring.ring_fd = 0;\n     ngx_file_aio = 0;\n }\n \n@@ -549,23 +472,11 @@ ngx_epoll_done(ngx_cycle_t *cycle)\n \n #if (NGX_HAVE_FILE_AIO)\n \n-    if (ngx_eventfd != -1) {\n-\n-        if (io_destroy(ngx_aio_ctx) == -1) {\n-            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n-                          \"io_destroy() failed\");\n-        }\n-\n-        if (close(ngx_eventfd) == -1) {\n-            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n-                          \"eventfd close() failed\");\n-        }\n-\n-        ngx_eventfd = -1;\n+    if (ngx_ring.ring_fd != 0) {\n+        io_uring_queue_exit(&ngx_ring);\n+        ngx_ring.ring_fd = 0;\n     }\n \n-    ngx_aio_ctx = 0;\n-\n #endif\n \n     ngx_free(event_list);\n@@ -939,84 +850,36 @@ ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)\n #if (NGX_HAVE_FILE_AIO)\n \n static void\n-ngx_epoll_eventfd_handler(ngx_event_t *ev)\n+ngx_epoll_io_uring_handler(ngx_event_t *ev)\n {\n-    int               n, events;\n-    long              i;\n-    uint64_t          ready;\n-    ngx_err_t         err;\n     ngx_event_t      *e;\n+    struct io_uring_cqe  *cqe;\n+    unsigned head;\n+    unsigned cqe_count = 0;\n     ngx_event_aio_t  *aio;\n-    struct io_event   event[64];\n-    struct timespec   ts;\n \n-    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"eventfd handler\");\n+    ngx_log_debug(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n+                   \"io_uring_peek_cqe: START\");\n \n-    n = read(ngx_eventfd, &ready, 8);\n+    io_uring_for_each_cqe(&ngx_ring, head, cqe) {\n+        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n+                       \"io_event: %p %d %d\",\n+                       cqe->user_data, cqe->res, cqe->flags);\n \n-    err = ngx_errno;\n+        e = (ngx_event_t *) io_uring_cqe_get_data(cqe);\n+        e->complete = 1;\n+        e->active = 0;\n+        e->ready = 1;\n \n-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"eventfd: %d\", n);\n+        aio = e->data;\n+        aio->res = cqe->res;\n \n-    if (n != 8) {\n-        if (n == -1) {\n-            if (err == NGX_EAGAIN) {\n-                return;\n-            }\n+        ++cqe_count;\n \n-            ngx_log_error(NGX_LOG_ALERT, ev->log, err, \"read(eventfd) failed\");\n-            return;\n-        }\n-\n-        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n-                      \"read(eventfd) returned only %d bytes\", n);\n-        return;\n+        ngx_post_event(e, &ngx_posted_events);\n     }\n \n-    ts.tv_sec = 0;\n-    ts.tv_nsec = 0;\n-\n-    while (ready) {\n-\n-        events = io_getevents(ngx_aio_ctx, 1, 64, event, &ts);\n-\n-        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n-                       \"io_getevents: %d\", events);\n-\n-        if (events > 0) {\n-            ready -= events;\n-\n-            for (i = 0; i < events; i++) {\n-\n-                ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n-                               \"io_event: %XL %XL %L %L\",\n-                                event[i].data, event[i].obj,\n-                                event[i].res, event[i].res2);\n-\n-                e = (ngx_event_t *) (uintptr_t) event[i].data;\n-\n-                e->complete = 1;\n-                e->active = 0;\n-                e->ready = 1;\n-\n-                aio = e->data;\n-                aio->res = event[i].res;\n-\n-                ngx_post_event(e, &ngx_posted_events);\n-            }\n-\n-            continue;\n-        }\n-\n-        if (events == 0) {\n-            return;\n-        }\n-\n-        /* events == -1 */\n-        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n-                      \"io_getevents() failed\");\n-        return;\n-    }\n+    io_uring_cq_advance(&ngx_ring, cqe_count);\n }\n \n #endif\ndiff --git a/src/event/ngx_event.h b/src/event/ngx_event.h\nindex 97f9673..eb17346 100644\n--- a/src/event/ngx_event.h\n+++ b/src/event/ngx_event.h\n@@ -160,7 +160,9 @@ struct ngx_event_aio_s {\n     size_t                     nbytes;\n #endif\n \n-    ngx_aiocb_t                aiocb;\n+    /* Make sure that this iov has the same lifecycle with its associated aio event */\n+    struct iovec               iov;\n+\n     ngx_event_t                event;\n };\n \ndiff --git a/src/os/unix/ngx_linux_aio_read.c b/src/os/unix/ngx_linux_aio_read.c\nindex 9f0a6c1..033bd56 100644\n--- a/src/os/unix/ngx_linux_aio_read.c\n+++ b/src/os/unix/ngx_linux_aio_read.c\n@@ -9,19 +9,15 @@\n #include <ngx_core.h>\n #include <ngx_event.h>\n \n+#include <liburing.h>\n \n-extern int            ngx_eventfd;\n-extern aio_context_t  ngx_aio_ctx;\n \n+extern struct io_uring          ngx_ring;\n+extern struct io_uring_params   ngx_ring_params;\n \n-static void ngx_file_aio_event_handler(ngx_event_t *ev);\n \n+static void ngx_file_aio_event_handler(ngx_event_t *ev);\n \n-static int\n-io_submit(aio_context_t ctx, long n, struct iocb **paiocb)\n-{\n-    return syscall(SYS_io_submit, ctx, n, paiocb);\n-}\n \n \n ngx_int_t\n@@ -50,10 +46,10 @@ ssize_t\n ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,\n     ngx_pool_t *pool)\n {\n-    ngx_err_t         err;\n-    struct iocb      *piocb[1];\n-    ngx_event_t      *ev;\n-    ngx_event_aio_t  *aio;\n+    ngx_err_t             err;\n+    ngx_event_t          *ev;\n+    ngx_event_aio_t      *aio;\n+    struct io_uring_sqe  *sqe;\n \n     if (!ngx_file_aio) {\n         return ngx_read_file(file, buf, size, offset);\n@@ -93,22 +89,41 @@ ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,\n         return NGX_ERROR;\n     }\n \n-    ngx_memzero(&aio->aiocb, sizeof(struct iocb));\n+    sqe = io_uring_get_sqe(&ngx_ring);\n \n-    aio->aiocb.aio_data = (uint64_t) (uintptr_t) ev;\n-    aio->aiocb.aio_lio_opcode = IOCB_CMD_PREAD;\n-    aio->aiocb.aio_fildes = file->fd;\n-    aio->aiocb.aio_buf = (uint64_t) (uintptr_t) buf;\n-    aio->aiocb.aio_nbytes = size;\n-    aio->aiocb.aio_offset = offset;\n-    aio->aiocb.aio_flags = IOCB_FLAG_RESFD;\n-    aio->aiocb.aio_resfd = ngx_eventfd;\n+    if (!sqe) {\n+        ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,\n+                       \"aio no sqe left:%d @%O:%uz %V\",\n+                       ev->complete, offset, size, &file->name);\n+        return ngx_read_file(file, buf, size, offset);\n+    }\n \n-    ev->handler = ngx_file_aio_event_handler;\n+    if (__builtin_expect(!!(ngx_ring_params.features & IORING_FEAT_CUR_PERSONALITY), 1)) {\n+        /*\n+         * `io_uring_prep_read` is faster than `io_uring_prep_readv`, because the kernel\n+         * doesn't need to import iovecs in advance.\n+         *\n+         * If the kernel supports `IORING_FEAT_CUR_PERSONALITY`, it should support\n+         * non-vectored read/write commands too.\n+         *\n+         * It's not perfect, but avoids an extra feature-test syscall.\n+         */\n+        io_uring_prep_read(sqe, file->fd, buf, size, offset);\n+    } else {\n+        /*\n+         * We must store iov into heap to prevent kernel from returning -EFAULT\n+         * in case `IORING_FEAT_SUBMIT_STABLE` is not supported\n+         */\n+        aio->iov.iov_base = buf;\n+        aio->iov.iov_len = size;\n+        io_uring_prep_readv(sqe, file->fd, &aio->iov, 1, offset);\n+    }\n+    io_uring_sqe_set_data(sqe, ev);\n \n-    piocb[0] = &aio->aiocb;\n \n-    if (io_submit(ngx_aio_ctx, 1, piocb) == 1) {\n+    ev->handler = ngx_file_aio_event_handler;\n+\n+    if (io_uring_submit(&ngx_ring) == 1) {\n         ev->active = 1;\n         ev->ready = 0;\n         ev->complete = 0;\ndiff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h\nindex 3036cae..0e461dd 100644\n--- a/src/os/unix/ngx_linux_config.h\n+++ b/src/os/unix/ngx_linux_config.h\n@@ -93,10 +93,6 @@ extern ssize_t sendfile(int s, int fd, int32_t *offset, size_t size);\n #include <sys/eventfd.h>\n #endif\n #include <sys/syscall.h>\n-#if (NGX_HAVE_FILE_AIO)\n-#include <linux/aio_abi.h>\n-typedef struct iocb  ngx_aiocb_t;\n-#endif\n \n \n #if (NGX_HAVE_CAPABILITIES)\n-- \n2.25.1\n"
  },
  {
    "path": "nginx_with_quic.patch",
    "content": "Add HTTP3(QUIC) Support.\nAdd HTTP2 HPACK Encoding Support.\nAdd Dynamic TLS Record Support.\n\nUsing: patch -p1 < nginx_with_quic.patch\nBased on cloudflare/quiche c9311a1.\nRequires nginx 1.21.4 or later to patch.\n\ndiff --color -uNr a/auto/lib/conf b/auto/lib/conf\n--- a/auto/lib/conf\t2022-12-13 23:53:53.000000000 +0800\n+++ b/auto/lib/conf\t2023-03-12 11:07:31.415411871 +0800\n@@ -25,6 +25,10 @@\n     . auto/lib/openssl/conf\n fi\n \n+if [ $USE_QUICHE = YES ]; then\n+    . auto/lib/quiche/conf\n+fi\n+\n if [ $USE_ZLIB = YES ]; then\n     . auto/lib/zlib/conf\n fi\ndiff --color -uNr a/auto/lib/make b/auto/lib/make\n--- a/auto/lib/make\t2022-12-13 23:53:53.000000000 +0800\n+++ b/auto/lib/make\t2023-03-12 11:07:31.415411871 +0800\n@@ -11,6 +11,10 @@\n     . auto/lib/openssl/make\n fi\n \n+if [ $QUICHE != NONE -a $QUICHE != NO -a $QUICHE != YES ]; then\n+    . auto/lib/quiche/make\n+fi\n+\n if [ $ZLIB != NONE -a $ZLIB != NO -a $ZLIB != YES ]; then\n     . auto/lib/zlib/make\n fi\ndiff --color -uNr a/auto/lib/openssl/make b/auto/lib/openssl/make\n--- a/auto/lib/openssl/make\t2022-12-13 23:53:53.000000000 +0800\n+++ b/auto/lib/openssl/make\t2023-03-12 11:07:31.415411871 +0800\n@@ -49,11 +49,13 @@\n         cat << END                                            >> $NGX_MAKEFILE\n \n $OPENSSL/.openssl/include/openssl/ssl.h:\t$NGX_MAKEFILE\n-\tcd $OPENSSL \\\\\n-\t&& if [ -f Makefile ]; then \\$(MAKE) clean; fi \\\\\n-\t&& ./config --prefix=$ngx_prefix no-shared no-threads $OPENSSL_OPT \\\\\n-\t&& \\$(MAKE) \\\\\n-\t&& \\$(MAKE) install_sw LIBDIR=lib\n+\tmkdir -p $OPENSSL/build $OPENSSL/.openssl/lib $OPENSSL/.openssl/include/openssl \\\\\n+\t&& cd $OPENSSL/build \\\\\n+\t&& cmake -DCMAKE_C_FLAGS=\"$OPENSSL_OPT\" -DCMAKE_CXX_FLAGS=\"$OPENSSL_OPT\" .. \\\\\n+\t&& \\$(MAKE) VERBOSE=1 \\\\\n+\t&& cd .. \\\\\n+\t&& cp -r src/include/openssl/*.h .openssl/include/openssl \\\\\n+\t&& cp build/libssl.a build/libcrypto.a .openssl/lib\n \n END\n \ndiff --color -uNr a/auto/lib/quiche/conf b/auto/lib/quiche/conf\n--- a/auto/lib/quiche/conf\t1970-01-01 08:00:00.000000000 +0800\n+++ b/auto/lib/quiche/conf\t2023-03-12 11:07:31.415411871 +0800\n@@ -0,0 +1,23 @@\n+\n+# Copyright (C) Cloudflare, Inc.\n+\n+\n+if [ $QUICHE != NONE ]; then\n+\n+    have=NGX_QUIC . auto/have\n+\n+    QUICHE_BUILD_TARGET=\"release\"\n+\n+    if [ $NGX_DEBUG = YES ]; then\n+        QUICHE_BUILD_TARGET=\"debug\"\n+    fi\n+\n+    CORE_INCS=\"$CORE_INCS $QUICHE/quiche/include\"\n+    CORE_DEPS=\"$CORE_DEPS $QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a\"\n+    CORE_LIBS=\"$CORE_LIBS $QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a $NGX_LIBPTHREAD -lm\"\n+\n+    if [ \"$NGX_SYSTEM\" = \"Darwin\" ]; then\n+        CORE_LIBS+=\" -framework Security\"\n+    fi\n+\n+fi\ndiff --color -uNr a/auto/lib/quiche/make b/auto/lib/quiche/make\n--- a/auto/lib/quiche/make\t1970-01-01 08:00:00.000000000 +0800\n+++ b/auto/lib/quiche/make\t2023-03-12 11:07:31.415411871 +0800\n@@ -0,0 +1,23 @@\n+\n+# Copyright (C) Cloudflare, Inc.\n+\n+QUICHE_COMMON_FLAGS=\"--package quiche --verbose --no-default-features --features ffi\"\n+\n+# Default is release build\n+QUICHE_BUILD_FLAGS=\"$QUICHE_COMMON_FLAGS --release\"\n+QUICHE_BUILD_TARGET=\"release\"\n+\n+if [ $NGX_DEBUG = YES ]; then\n+    QUICHE_BUILD_FLAGS=\"$QUICHE_COMMON_FLAGS\"\n+    QUICHE_BUILD_TARGET=\"debug\"\n+fi\n+\n+\n+cat << END                                                    >> $NGX_MAKEFILE\n+\n+$QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a: \\\\\n+\t\t$OPENSSL/.openssl/include/openssl/ssl.h \\\\\n+\t\t$NGX_MAKEFILE\n+\tcd $QUICHE && cargo build $QUICHE_BUILD_FLAGS $QUICHE_OPT\n+\n+END\ndiff --color -uNr a/auto/make b/auto/make\n--- a/auto/make\t2022-12-13 23:53:53.000000000 +0800\n+++ b/auto/make\t2023-03-12 11:07:31.415411871 +0800\n@@ -7,7 +7,8 @@\n \n mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \\\n          $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \\\n-         $NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/modules \\\n+         $NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/v3 \\\n+         $NGX_OBJS/src/http/modules \\\n          $NGX_OBJS/src/http/modules/perl \\\n          $NGX_OBJS/src/mail \\\n          $NGX_OBJS/src/stream \\\ndiff --color -uNr a/auto/modules b/auto/modules\n--- a/auto/modules\t2022-12-13 23:53:53.000000000 +0800\n+++ b/auto/modules\t2023-03-12 11:07:39.510625293 +0800\n@@ -124,6 +124,7 @@\n     #     ngx_http_header_filter\n     #     ngx_http_chunked_filter\n     #     ngx_http_v2_filter\n+    #     ngx_http_v3_filter\n     #     ngx_http_range_header_filter\n     #     ngx_http_gzip_filter\n     #     ngx_http_postpone_filter\n@@ -156,6 +157,7 @@\n                       ngx_http_header_filter_module \\\n                       ngx_http_chunked_filter_module \\\n                       ngx_http_v2_filter_module \\\n+                      ngx_http_v3_filter_module \\\n                       ngx_http_range_header_filter_module \\\n                       ngx_http_gzip_filter_module \\\n                       ngx_http_postpone_filter_module \\\n@@ -217,6 +219,17 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_V3 = YES ]; then\n+        ngx_module_name=ngx_http_v3_filter_module\n+        ngx_module_incs=\n+        ngx_module_deps=\n+        ngx_module_srcs=src/http/v3/ngx_http_v3_filter_module.c\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_V3\n+\n+        . auto/module\n+    fi\n+\n     if :; then\n         ngx_module_name=ngx_http_range_header_filter_module\n         ngx_module_incs=\n@@ -426,6 +439,28 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_V3 = YES ]; then\n+        USE_QUICHE=YES\n+        USE_OPENSSL=YES\n+        have=NGX_HTTP_V3 . auto/have\n+        have=NGX_HTTP_HEADERS . auto/have\n+\n+        ngx_module_name=ngx_http_v3_module\n+        ngx_module_incs=src/http/v3\n+        ngx_module_deps=\"src/http/v3/ngx_http_v3.h \\\n+                         src/http/v3/ngx_http_v3_module.h\"\n+        ngx_module_srcs=\"src/http/v3/ngx_http_v3.c \\\n+                         src/http/v3/ngx_http_v3_module.c\"\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_V3\n+\n+        . auto/module\n+    fi\n+\n+    if [ $HTTP_V2_HPACK_ENC = YES ]; then\n+        have=NGX_HTTP_V2_HPACK_ENC . auto/have\n+    fi\n+\n     if :; then\n         ngx_module_name=ngx_http_static_module\n         ngx_module_incs=\n@@ -1270,6 +1305,19 @@\n \n     . auto/module\n fi\n+\n+\n+if [ $USE_QUICHE = YES ]; then\n+    ngx_module_type=CORE\n+    ngx_module_name=ngx_quic_module\n+    ngx_module_incs=\n+    ngx_module_deps=src/event/ngx_event_quic.h\n+    ngx_module_srcs=src/event/ngx_event_quic.c\n+    ngx_module_libs=\n+    ngx_module_link=YES\n+\n+    . auto/module\n+fi\n \n \n if [ $USE_PCRE = YES ]; then\ndiff --color -uNr a/auto/options b/auto/options\n--- a/auto/options\t2022-12-13 23:53:53.000000000 +0800\n+++ b/auto/options\t2023-03-12 11:07:39.511625319 +0800\n@@ -59,6 +59,8 @@\n HTTP_GZIP=YES\n HTTP_SSL=NO\n HTTP_V2=NO\n+HTTP_V2_HPACK_ENC=NO\n+HTTP_V3=NO\n HTTP_SSI=YES\n HTTP_REALIP=NO\n HTTP_XSLT=NO\n@@ -151,6 +153,9 @@\n USE_OPENSSL=NO\n OPENSSL=NONE\n \n+USE_QUICHE=NO\n+QUICHE=NONE\n+\n USE_ZLIB=NO\n ZLIB=NONE\n ZLIB_OPT=\n@@ -228,6 +233,8 @@\n \n         --with-http_ssl_module)          HTTP_SSL=YES               ;;\n         --with-http_v2_module)           HTTP_V2=YES                ;;\n+        --with-http_v2_hpack_enc)        HTTP_V2_HPACK_ENC=YES      ;;\n+        --with-http_v3_module)           HTTP_V3=YES                ;;\n         --with-http_realip_module)       HTTP_REALIP=YES            ;;\n         --with-http_addition_module)     HTTP_ADDITION=YES          ;;\n         --with-http_xslt_module)         HTTP_XSLT=YES              ;;\n@@ -363,6 +370,9 @@\n         --with-openssl=*)                OPENSSL=\"$value\"           ;;\n         --with-openssl-opt=*)            OPENSSL_OPT=\"$value\"       ;;\n \n+        --with-quiche=*)                 QUICHE=\"$value\"           ;;\n+        --with-quiche-opt=*)             QUICHE_OPT=\"$value\"       ;;\n+\n         --with-md5=*)\n             NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n $0: warning: the \\\"--with-md5\\\" option is deprecated\"\n@@ -445,6 +455,8 @@\n \n   --with-http_ssl_module             enable ngx_http_ssl_module\n   --with-http_v2_module              enable ngx_http_v2_module\n+  --with-http_v2_hpack_enc           enable ngx_http_v2_hpack_enc\n+  --with-http_v3_module              enable ngx_http_v3_module\n   --with-http_realip_module          enable ngx_http_realip_module\n   --with-http_addition_module        enable ngx_http_addition_module\n   --with-http_xslt_module            enable ngx_http_xslt_module\ndiff --color -uNr a/src/core/ngx_connection.h b/src/core/ngx_connection.h\n--- a/src/core/ngx_connection.h\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/core/ngx_connection.h\t2023-03-12 11:07:31.416411897 +0800\n@@ -77,6 +77,9 @@\n     unsigned            deferred_accept:1;\n     unsigned            delete_deferred:1;\n     unsigned            add_deferred:1;\n+#if (NGX_QUIC)\n+    unsigned            quic:1;\n+#endif\n #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n     char               *accept_filter;\n #endif\n@@ -153,6 +156,10 @@\n \n     ngx_udp_connection_t  *udp;\n \n+#if (NGX_QUIC)\n+    ngx_quic_connection_t *quic;\n+#endif\n+\n     struct sockaddr    *local_sockaddr;\n     socklen_t           local_socklen;\n \ndiff --color -uNr a/src/core/ngx_core.h b/src/core/ngx_core.h\n--- a/src/core/ngx_core.h\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/core/ngx_core.h\t2023-03-12 11:07:31.416411897 +0800\n@@ -83,6 +83,9 @@\n #if (NGX_OPENSSL)\n #include <ngx_event_openssl.h>\n #endif\n+#if (NGX_QUIC)\n+#include <ngx_event_quic.h>\n+#endif\n #include <ngx_process_cycle.h>\n #include <ngx_conf_file.h>\n #include <ngx_module.h>\ndiff --color -uNr a/src/core/ngx_murmurhash.c b/src/core/ngx_murmurhash.c\n--- a/src/core/ngx_murmurhash.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/core/ngx_murmurhash.c\t2023-03-12 11:07:39.511625319 +0800\n@@ -50,3 +50,63 @@\n \n     return h;\n }\n+\n+\n+uint64_t\n+ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed)\n+{\n+    uint64_t  h, k;\n+\n+    h = seed ^ len;\n+\n+    while (len >= 8) {\n+        k  = data[0];\n+        k |= data[1] << 8;\n+        k |= data[2] << 16;\n+        k |= data[3] << 24;\n+        k |= (uint64_t)data[4] << 32;\n+        k |= (uint64_t)data[5] << 40;\n+        k |= (uint64_t)data[6] << 48;\n+        k |= (uint64_t)data[7] << 56;\n+\n+        k *= 0xc6a4a7935bd1e995ull;\n+        k ^= k >> 47;\n+        k *= 0xc6a4a7935bd1e995ull;\n+\n+        h ^= k;\n+        h *= 0xc6a4a7935bd1e995ull;\n+\n+        data += 8;\n+        len -= 8;\n+    }\n+\n+    switch (len) {\n+    case 7:\n+        h ^= (uint64_t)data[6] << 48;\n+        /* fall through */\n+    case 6:\n+        h ^= (uint64_t)data[5] << 40;\n+        /* fall through */\n+    case 5:\n+        h ^= (uint64_t)data[4] << 32;\n+        /* fall through */\n+    case 4:\n+        h ^= data[3] << 24;\n+        /* fall through */\n+    case 3:\n+        h ^= data[2] << 16;\n+        /* fall through */\n+    case 2:\n+        h ^= data[1] << 8;\n+        /* fall through */\n+    case 1:\n+        h ^= data[0];\n+        h *= 0xc6a4a7935bd1e995ull;\n+    }\n+\n+    h ^= h >> 47;\n+    h *= 0xc6a4a7935bd1e995ull;\n+    h ^= h >> 47;\n+\n+    return h;\n+}\ndiff --color -uNr a/src/core/ngx_murmurhash.h b/src/core/ngx_murmurhash.h\n--- a/src/core/ngx_murmurhash.h\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/core/ngx_murmurhash.h\t2023-03-12 11:07:39.511625319 +0800\n@@ -15,5 +15,7 @@\n \n uint32_t ngx_murmur_hash2(u_char *data, size_t len);\n \n+uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed);\n+\n \n #endif /* _NGX_MURMURHASH_H_INCLUDED_ */\ndiff --color -uNr a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c\n--- a/src/event/ngx_event_openssl.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/event/ngx_event_openssl.c\t2023-03-12 11:07:39.512625346 +0800\n@@ -1677,6 +1677,7 @@\n \n     sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);\n     sc->buffer_size = ssl->buffer_size;\n+    sc->dyn_rec = ssl->dyn_rec;\n \n     sc->session_ctx = ssl->ctx;\n \n@@ -2648,6 +2649,41 @@\n \n     for ( ;; ) {\n \n+        /* Dynamic record resizing:\n+           We want the initial records to fit into one TCP segment\n+           so we don't get TCP HoL blocking due to TCP Slow Start.\n+           A connection always starts with small records, but after\n+           a given amount of records sent, we make the records larger\n+           to reduce header overhead.\n+           After a connection has idled for a given timeout, begin\n+           the process from the start. The actual parameters are\n+           configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */\n+\n+        if (c->ssl->dyn_rec.timeout > 0 ) {\n+\n+            if (ngx_current_msec - c->ssl->dyn_rec_last_write >\n+                c->ssl->dyn_rec.timeout)\n+            {\n+                buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                c->ssl->dyn_rec_records_sent = 0;\n+\n+            } else {\n+                if (c->ssl->dyn_rec_records_sent >\n+                    c->ssl->dyn_rec.threshold * 2)\n+                {\n+                    buf->end = buf->start + c->ssl->buffer_size;\n+\n+                } else if (c->ssl->dyn_rec_records_sent >\n+                           c->ssl->dyn_rec.threshold)\n+                {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_hi;\n+\n+                } else {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                }\n+            }\n+        }\n+\n         while (in && buf->last < buf->end && send < limit) {\n             if (in->buf->last_buf || in->buf->flush) {\n                 flush = 1;\n@@ -2787,6 +2823,9 @@\n \n     if (n > 0) {\n \n+        c->ssl->dyn_rec_records_sent++;\n+        c->ssl->dyn_rec_last_write = ngx_current_msec;\n+\n         if (c->ssl->saved_read_handler) {\n \n             c->read->handler = c->ssl->saved_read_handler;\ndiff --color -uNr a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h\n--- a/src/event/ngx_event_openssl.h\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/event/ngx_event_openssl.h\t2023-03-12 11:07:39.513625372 +0800\n@@ -78,10 +78,19 @@\n typedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;\n \n \n+typedef struct {\n+    ngx_msec_t                  timeout;\n+    ngx_uint_t                  threshold;\n+    size_t                      size_lo;\n+    size_t                      size_hi;\n+} ngx_ssl_dyn_rec_t;\n+\n+\n struct ngx_ssl_s {\n     SSL_CTX                    *ctx;\n     ngx_log_t                  *log;\n     size_t                      buffer_size;\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n };\n \n \n@@ -120,6 +129,10 @@\n     unsigned                    in_ocsp:1;\n     unsigned                    early_preread:1;\n     unsigned                    write_blocked:1;\n+\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n+    ngx_msec_t                  dyn_rec_last_write;\n+    ngx_uint_t                  dyn_rec_records_sent;\n };\n \n \n@@ -129,7 +142,7 @@\n #define NGX_SSL_DFLT_BUILTIN_SCACHE  -5\n \n \n-#define NGX_SSL_MAX_SESSION_SIZE  4096\n+#define NGX_SSL_MAX_SESSION_SIZE  16384\n \n typedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;\n \ndiff --color -uNr a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c\n--- a/src/event/ngx_event_quic.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/event/ngx_event_quic.c\t2023-03-12 11:07:31.417411924 +0800\n@@ -0,0 +1,625 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_event.h>\n+\n+\n+/* Limit outgoing packets to 1200 bytes. This is the minimum value allowed. */\n+#define MAX_DATAGRAM_SIZE 1200\n+\n+/* errors */\n+#define NGX_QUIC_NO_ERROR  0x0\n+#define NGX_QUIC_INTERNAL_ERROR  0x1\n+\n+\n+static void ngx_quic_read_handler(ngx_event_t *ev);\n+static void ngx_quic_write_handler(ngx_event_t *ev);\n+\n+static void ngx_quic_set_timer(ngx_connection_t *c);\n+\n+static void ngx_quic_handshake_completed(ngx_connection_t *c);\n+\n+static void ngx_quic_shutdown_handler(ngx_event_t *ev);\n+\n+static void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t status);\n+static void ngx_quic_close_connection(ngx_connection_t *c);\n+\n+static ngx_int_t ngx_quic_send_udp_packet(ngx_connection_t *c, uint8_t *buf,\n+    size_t len);\n+\n+\n+static ngx_command_t  ngx_quic_commands[] = {\n+\n+    ngx_null_command\n+};\n+\n+\n+static ngx_core_module_t  ngx_quic_module_ctx = {\n+    ngx_string(\"quic\"),\n+    NULL,\n+    NULL\n+};\n+\n+\n+ngx_module_t  ngx_quic_module = {\n+    NGX_MODULE_V1,\n+    &ngx_quic_module_ctx,                  /* module context */\n+    ngx_quic_commands,                     /* module directives */\n+    NGX_CORE_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+ngx_int_t\n+ngx_quic_create_conf(ngx_quic_t *quic)\n+{\n+    quic->config = quiche_config_new(QUICHE_PROTOCOL_VERSION);\n+    if (quic->config == NULL) {\n+        ngx_log_error(NGX_LOG_EMERG, quic->log, 0, \"failed to create quic config\");\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_validate_initial(ngx_event_t *ev, u_char *buf, ssize_t buf_len)\n+{\n+    /* Check incoming packet type, if it's not Initial we shouldn't be here. */\n+    if (((buf[0] & 0x30) >> 4) != 0) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n+                       \"packet is not quic client initial\");\n+        return NGX_ERROR;\n+    }\n+\n+    /* Client Initial packets must be at least 1200 bytes. */\n+    if (buf_len < QUICHE_MIN_CLIENT_INITIAL_LEN) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n+                       \"quic initial packet is too short\");\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_create_connection(ngx_quic_t *quic, ngx_connection_t *c)\n+{\n+    int                     rc;\n+    u_char                 *buf;\n+    size_t                  buf_len;\n+    quiche_conn            *conn;\n+    static uint8_t          out[MAX_DATAGRAM_SIZE];\n+\n+    uint8_t                 pkt_type;\n+    uint32_t                pkt_version;\n+\n+    uint8_t                 scid[QUICHE_MAX_CONN_ID_LEN];\n+    size_t                  scid_len = sizeof(scid);\n+\n+    uint8_t                 dcid[QUICHE_MAX_CONN_ID_LEN];\n+    size_t                  dcid_len = sizeof(dcid);\n+\n+    uint8_t                 token[1];\n+    size_t                  token_len = sizeof(token);\n+\n+    ngx_quic_connection_t  *qc;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic init connection\");\n+\n+    /* Extract some fields from the client's Initial packet, which was saved\n+     * into c->buffer by ngx_event_recvmsg(). */\n+    buf = c->buffer->pos;\n+    buf_len = ngx_buf_size(c->buffer);\n+\n+    rc = quiche_header_info(buf, buf_len, QUICHE_MAX_CONN_ID_LEN,\n+                            &pkt_version, &pkt_type,\n+                            scid, &scid_len, dcid, &dcid_len,\n+                            token, &token_len);\n+    if (rc < 0) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"failed to parse quic header: %d\", rc);\n+        return NGX_ERROR;\n+    }\n+\n+    /* Version mismatch, do version negotiation. */\n+    if (!quiche_version_is_supported(pkt_version)) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic version negotiation\");\n+\n+        ssize_t written = quiche_negotiate_version(scid, scid_len,\n+                                                   dcid, dcid_len,\n+                                                   out, sizeof(out));\n+\n+        if (written < 0) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                          \"failed to create quic vneg packet: %d\", written);\n+            return NGX_ERROR;\n+        }\n+\n+        if (ngx_quic_send_udp_packet(c, out, written) == NGX_ERROR) {\n+            return NGX_ERROR;\n+        }\n+\n+        return NGX_DONE;\n+    }\n+\n+    /* Initialize source connection ID with some random bytes. */\n+    RAND_bytes(scid, sizeof(scid));\n+\n+#if (NGX_DEBUG)\n+    {\n+    uint8_t dcid_hex[QUICHE_MAX_CONN_ID_LEN * 2],\n+            scid_hex[QUICHE_MAX_CONN_ID_LEN * 2];\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+        \"new quic connection dcid:%*.s new_scid:%*.s\",\n+        ngx_hex_dump(dcid_hex, dcid, dcid_len) - dcid_hex, dcid_hex,\n+        ngx_hex_dump(scid_hex, scid, sizeof(scid)) - scid_hex, scid_hex);\n+    }\n+#endif\n+\n+    conn = quiche_conn_new_with_tls(scid, sizeof(scid), NULL, 0,\n+                                    c->local_sockaddr, c->local_socklen,\n+                                    c->sockaddr, c->socklen, quic->config,\n+                                    c->ssl->connection, true);\n+    if (conn == NULL) {\n+        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to create quic connection\");\n+        return NGX_ERROR;\n+    }\n+\n+    qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t));\n+    if (qc == NULL) {\n+        quiche_conn_free(conn);\n+        return NGX_ERROR;\n+    }\n+\n+    qc->handler = NULL;\n+\n+    qc->conn = conn;\n+\n+    c->quic = qc;\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_handshake(ngx_connection_t *c)\n+{\n+    u_char   *buf;\n+    size_t    buf_len;\n+    ssize_t   done;\n+\n+    quiche_recv_info recv_info = {\n+        c->sockaddr,\n+        c->socklen,\n+        c->local_sockaddr,\n+        c->local_socklen,\n+    };\n+\n+    /* Process the client's Initial packet, which was saved into c->buffer by\n+     * ngx_event_recvmsg(). */\n+    buf = c->buffer->pos;\n+    buf_len = ngx_buf_size(c->buffer);\n+\n+    done = quiche_conn_recv(c->quic->conn, buf, buf_len, &recv_info);\n+\n+    if ((done < 0) && (done != QUICHE_ERR_DONE)) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                      \"failed to process quic packet: %d\", done);\n+        return NGX_ERROR;\n+    }\n+\n+    c->read->handler = ngx_quic_read_handler;\n+    c->write->handler = ngx_quic_write_handler;\n+\n+    ngx_post_event(c->write, &ngx_posted_events);\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static void\n+ngx_quic_read_handler(ngx_event_t *rev)\n+{\n+    int                n;\n+    static uint8_t     buf[65535];\n+    ngx_connection_t  *c;\n+\n+    c = rev->data;\n+\n+    c->log->action = \"reading QUIC packets\";\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic read handler\");\n+\n+    if (rev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic connection timed out\");\n+\n+        if (c->quic->handler != NULL) {\n+            c->quic->handler(c);\n+        }\n+\n+        return;\n+    }\n+\n+    for (;;) {\n+        n = c->recv(c, buf, sizeof(buf));\n+        if (n == NGX_AGAIN) {\n+            break;\n+        }\n+\n+        if (n == NGX_ERROR) {\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+\n+        quiche_recv_info recv_info = {\n+            c->sockaddr,\n+            c->socklen,\n+            c->local_sockaddr,\n+            c->local_socklen,\n+        };\n+\n+        ssize_t done = quiche_conn_recv(c->quic->conn, buf, n, &recv_info);\n+\n+        if (done == QUICHE_ERR_DONE) {\n+            break;\n+        }\n+\n+        if (done < 0) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"failed to process quic packet: %d\", done);\n+\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+    }\n+\n+    if (quiche_conn_is_in_early_data(c->quic->conn) ||\n+            quiche_conn_is_established(c->quic->conn)) {\n+        if (!c->ssl->handshaked) {\n+            ngx_quic_handshake_completed(c);\n+        }\n+\n+        if ((c->quic == NULL) || (c->quic->handler == NULL)) {\n+            return;\n+        }\n+\n+        /* Notify application layer that there might be stream data to read. */\n+        c->quic->handler(c);\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic done reading\");\n+\n+    ngx_post_event(c->write, &ngx_posted_events);\n+}\n+\n+\n+static void\n+ngx_quic_write_handler(ngx_event_t *wev)\n+{\n+    ngx_connection_t   *c;\n+    quiche_send_info    send_info;\n+    static uint8_t      out[MAX_DATAGRAM_SIZE];\n+\n+    c = wev->data;\n+\n+    c->log->action = \"writing QUIC packets\";\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic write handler\");\n+\n+    if (wev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic alarm fired\");\n+\n+        quiche_conn_on_timeout(c->quic->conn);\n+    }\n+\n+    if (quiche_conn_is_closed(c->quic->conn)) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic connection is closed\");\n+\n+        ngx_quic_finalize_connection(c, NGX_QUIC_NO_ERROR);\n+        return;\n+    }\n+\n+    for (;;) {\n+        ssize_t written = quiche_conn_send(c->quic->conn, out, sizeof(out),\n+                                           &send_info);\n+\n+        if (written == QUICHE_ERR_DONE) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic done writing\");\n+            break;\n+        }\n+\n+        if (written < 0) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"failed to create quic packet: %d\", written);\n+\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+\n+        int rc = ngx_quic_send_udp_packet(c, out, written);\n+\n+        if (rc == NGX_AGAIN) {\n+            break;\n+        }\n+\n+        if (rc == NGX_ERROR) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"failed to send quic packet\");\n+\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+    }\n+\n+    ngx_quic_set_timer(c);\n+}\n+\n+\n+static void\n+ngx_quic_set_timer(ngx_connection_t *c)\n+{\n+    uint64_t      expiry;\n+    ngx_event_t  *wev;\n+\n+    wev = c->write;\n+\n+    expiry = quiche_conn_timeout_as_millis(c->quic->conn);\n+    expiry = ngx_max(expiry, 1);\n+\n+    if (wev->timer_set) {\n+        ngx_del_timer(wev);\n+    }\n+\n+    /* quiche_conn_timeout_as_millis() will return UINT64_MAX when the timer\n+     * should be unset (this would be equvalent to returning Option::None in\n+     * Rust). To avoid overflow we need to explicitly check for this value. */\n+    if (expiry != UINT64_MAX) {\n+        ngx_add_timer(wev, (ngx_msec_t)expiry);\n+    }\n+}\n+\n+\n+static void\n+ngx_quic_handshake_completed(ngx_connection_t *c)\n+{\n+#if (NGX_DEBUG)\n+    {\n+    char         buf[129], *s, *d;\n+#if OPENSSL_VERSION_NUMBER >= 0x10000000L\n+    const\n+#endif\n+    SSL_CIPHER  *cipher;\n+\n+    cipher = SSL_get_current_cipher(c->ssl->connection);\n+\n+    if (cipher) {\n+        SSL_CIPHER_description(cipher, &buf[1], 128);\n+\n+        for (s = &buf[1], d = buf; *s; s++) {\n+            if (*s == ' ' && *d == ' ') {\n+                continue;\n+            }\n+\n+            if (*s == LF || *s == CR) {\n+                continue;\n+            }\n+\n+            *++d = *s;\n+        }\n+\n+        if (*d != ' ') {\n+            d++;\n+        }\n+\n+        *d = '\\0';\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"QUIC: %s, cipher: \\\"%s\\\"\",\n+                       SSL_get_version(c->ssl->connection), &buf[1]);\n+\n+        if (SSL_session_reused(c->ssl->connection)) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"quic reused session\");\n+        }\n+\n+    } else {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic no shared ciphers\");\n+    }\n+    }\n+#endif\n+\n+    ngx_del_timer(c->read);\n+\n+    c->ssl->handshaked = 1;\n+\n+    /* Notify application layer that the handshake is complete. */\n+    c->ssl->handler(c);\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_shutdown(ngx_connection_t *c)\n+{\n+    ssize_t           written;\n+    quiche_send_info  send_info;\n+    static uint8_t    out[MAX_DATAGRAM_SIZE];\n+\n+    /* Connection is closed, free memory. */\n+    if (quiche_conn_is_closed(c->quic->conn)) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"free quic connection\");\n+\n+        quiche_conn_free(c->quic->conn);\n+\n+        c->quic = NULL;\n+        c->ssl = NULL;\n+\n+        return NGX_OK;\n+    }\n+\n+    /* We can't free the connection state yet, as we need to wait for the\n+     * draining timeout to expire.\n+     *\n+     * Setup event handlers such that we will try again when that happens (or\n+     * when another event is triggered). */\n+    c->read->handler = ngx_quic_shutdown_handler;\n+    c->write->handler = ngx_quic_shutdown_handler;\n+\n+    /* Try sending a packet in order to flush pending frames (CONNECTION_CLOSE\n+     * for example), but ignore errors as we are already closing the connection\n+     * anyway. */\n+    written = quiche_conn_send(c->quic->conn, out, sizeof(out), &send_info);\n+\n+    if (written > 0) {\n+        ngx_quic_send_udp_packet(c, out, written);\n+    }\n+\n+    ngx_quic_set_timer(c);\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static void\n+ngx_quic_shutdown_handler(ngx_event_t *ev)\n+{\n+    ngx_connection_t           *c;\n+    ngx_connection_handler_pt   handler;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"quic shutdown handler\");\n+\n+    c = ev->data;\n+    handler = c->quic->handler;\n+\n+    if (ev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic alarm fired\");\n+\n+        quiche_conn_on_timeout(c->quic->conn);\n+    }\n+\n+    if (ngx_quic_shutdown(c) == NGX_AGAIN) {\n+        return;\n+    }\n+\n+    handler(c);\n+}\n+\n+\n+static void\n+ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t status)\n+{\n+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                   \"finalize quic connection: %d\", c->fd);\n+\n+    c->error = 1;\n+\n+    if (quiche_conn_is_closed(c->quic->conn)) {\n+        c->close = 1;\n+    }\n+\n+    quiche_conn_close(c->quic->conn, false, status, NULL, 0);\n+\n+    /* Notify the application layer that the connection is in an error\n+     * state and will be closed. */\n+    if (c->quic->handler != NULL) {\n+        c->quic->handler(c);\n+        return;\n+    }\n+\n+    ngx_quic_close_connection(c);\n+}\n+\n+\n+static void\n+ngx_quic_close_connection(ngx_connection_t *c)\n+{\n+    ngx_pool_t  *pool;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                   \"close quic connection: %d\", c->fd);\n+\n+    if (c->quic) {\n+        if (ngx_quic_shutdown(c) == NGX_AGAIN) {\n+            c->quic->handler = ngx_quic_close_connection;\n+            return;\n+        }\n+    }\n+\n+#if (NGX_STAT_STUB)\n+    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);\n+#endif\n+\n+    c->destroyed = 1;\n+\n+    pool = c->pool;\n+\n+    ngx_close_connection(c);\n+\n+    ngx_destroy_pool(pool);\n+}\n+\n+\n+void\n+ngx_quic_cleanup_ctx(void *data)\n+{\n+    ngx_quic_t  *quic = data;\n+\n+    quiche_config_free(quic->config);\n+}\n+\n+\n+static ngx_int_t\n+ngx_quic_send_udp_packet(ngx_connection_t *c, uint8_t *buf, size_t len)\n+{\n+    ngx_buf_t     out_buf = {0};\n+    ngx_chain_t   out_chain = {0};\n+    ngx_chain_t  *cl;\n+\n+    /* The send_chain() API takes an ngx_chain_t parameter instead of a simple\n+     * buffer, so we need to initialize the chain such that it contains only a\n+     * single buffer.\n+     *\n+     * The c->send_chain() call is required (instead of just c->send()) because\n+     * it uses the sendmsg(2) syscall (instead of sendto(2)), which allows us to\n+     * specify the correct source IP address for the connection. */\n+\n+    out_buf.start = out_buf.pos = buf;\n+    out_buf.end = out_buf.last = buf + len;\n+    out_buf.memory = 1;\n+    out_buf.flush = 1;\n+\n+    out_chain.buf = &out_buf;\n+    out_chain.next = NULL;\n+\n+    c->write->ready = 1;\n+\n+    cl = c->send_chain(c, &out_chain, 0);\n+\n+    if (cl != NULL) {\n+        return NGX_AGAIN;\n+    }\n+\n+    if (cl == NGX_CHAIN_ERROR) {\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\ndiff --color -uNr a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h\n--- a/src/event/ngx_event_quic.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/event/ngx_event_quic.h\t2023-03-12 11:07:31.417411924 +0800\n@@ -0,0 +1,49 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#ifndef _NGX_EVENT_QUIC_H_INCLUDED_\n+#define _NGX_EVENT_QUIC_H_INCLUDED_\n+\n+\n+#include <stdbool.h>\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+\n+#include <quiche.h>\n+\n+typedef struct ngx_quic_s              ngx_quic_t;\n+typedef struct ngx_quic_connection_s   ngx_quic_connection_t;\n+\n+struct ngx_quic_s {\n+    quiche_config              *config;\n+    ngx_log_t                  *log;\n+};\n+\n+struct ngx_quic_connection_s {\n+    quiche_conn                *conn;\n+\n+    ngx_connection_handler_pt   handler;\n+};\n+\n+\n+ngx_int_t ngx_quic_create_conf(ngx_quic_t *quic);\n+\n+ngx_int_t ngx_quic_validate_initial(ngx_event_t *ev, u_char *buf,\n+    ssize_t buf_len);\n+\n+ngx_int_t ngx_quic_create_connection(ngx_quic_t *quic, ngx_connection_t *c);\n+\n+ngx_int_t ngx_quic_create_ssl_connection(ngx_ssl_t *ssl, ngx_connection_t *c,\n+    ngx_uint_t flags);\n+\n+ngx_int_t ngx_quic_handshake(ngx_connection_t *c);\n+\n+ngx_int_t ngx_quic_shutdown(ngx_connection_t *c);\n+\n+void ngx_quic_cleanup_ctx(void *data);\n+\n+#endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */\ndiff --color -uNr a/src/event/ngx_event_udp.c b/src/event/ngx_event_udp.c\n--- a/src/event/ngx_event_udp.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/event/ngx_event_udp.c\t2023-03-12 11:07:31.417411924 +0800\n@@ -204,6 +204,14 @@\n         (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);\n #endif\n \n+#if (NGX_QUIC)\n+        if (ls->quic) {\n+            if (ngx_quic_validate_initial(ev, buffer, n) != NGX_OK) {\n+                goto next;\n+            }\n+        }\n+#endif\n+\n         ngx_accept_disabled = ngx_cycle->connection_n / 8\n                               - ngx_cycle->free_connection_n;\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c\n--- a/src/http/modules/ngx_http_ssl_module.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.c\t2023-03-12 11:07:39.513625372 +0800\n@@ -296,6 +296,41 @@\n       offsetof(ngx_http_ssl_srv_conf_t, reject_handshake),\n       NULL },\n \n+    { ngx_string(\"ssl_dyn_rec_enable\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_flag_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_lo\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_hi\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_threshold\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold),\n+      NULL },\n+\n       ngx_null_command\n };\n \n@@ -424,7 +459,7 @@\n #if (NGX_DEBUG)\n     unsigned int            i;\n #endif\n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_V3)\n     ngx_http_connection_t  *hc;\n #endif\n #if (NGX_HTTP_V2 || NGX_DEBUG)\n@@ -441,14 +476,23 @@\n     }\n #endif\n \n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_V3)\n     hc = c->data;\n+#endif\n \n+#if (NGX_HTTP_V2)\n     if (hc->addr_conf->http2) {\n         srv = (unsigned char *) NGX_HTTP_V2_ALPN_PROTO NGX_HTTP_ALPN_PROTOS;\n         srvlen = sizeof(NGX_HTTP_V2_ALPN_PROTO NGX_HTTP_ALPN_PROTOS) - 1;\n     } else\n #endif\n+#if (NGX_HTTP_V3)\n+    if (hc->addr_conf->quic) {\n+        srv = (unsigned char *) QUICHE_H3_APPLICATION_PROTOCOL;\n+        srvlen = sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1;\n+\n+    } else\n+#endif\n     {\n         srv = (unsigned char *) NGX_HTTP_ALPN_PROTOS;\n         srvlen = sizeof(NGX_HTTP_ALPN_PROTOS) - 1;\n@@ -598,6 +642,11 @@\n     sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR;\n     sscf->stapling = NGX_CONF_UNSET;\n     sscf->stapling_verify = NGX_CONF_UNSET;\n+    sscf->dyn_rec_enable = NGX_CONF_UNSET;\n+    sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC;\n+    sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT;\n \n     return sscf;\n }\n@@ -673,6 +722,20 @@\n     ngx_conf_merge_str_value(conf->stapling_responder,\n                          prev->stapling_responder, \"\");\n \n+    ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0);\n+    ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout,\n+                             1000);\n+    /* Default sizes for the dynamic record sizes are defined to fit maximal\n+       TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi:\n+       1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo,\n+                             1369);\n+    /* 4229 = (1500 - 40 - 20 - 10) * 3  - 61 */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi,\n+                             4229);\n+    ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold,\n+                             40);\n+\n     conf->ssl.log = cf->log;\n \n     if (conf->enable) {\n@@ -899,6 +962,28 @@\n         return NGX_CONF_ERROR;\n     }\n \n+    if (conf->dyn_rec_enable) {\n+        conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout;\n+        conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold;\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_lo) {\n+            conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_lo = conf->buffer_size;\n+        }\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_hi) {\n+            conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_hi = conf->buffer_size;\n+        }\n+\n+    } else {\n+        conf->ssl.dyn_rec.timeout = 0;\n+    }\n+\n     return NGX_CONF_OK;\n }\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h\n--- a/src/http/modules/ngx_http_ssl_module.h\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.h\t2023-03-12 11:07:39.514625398 +0800\n@@ -67,6 +67,12 @@\n \n     u_char                         *file;\n     ngx_uint_t                      line;\n+\n+    ngx_flag_t                      dyn_rec_enable;\n+    ngx_msec_t                      dyn_rec_timeout;\n+    size_t                          dyn_rec_size_lo;\n+    size_t                          dyn_rec_size_hi;\n+    ngx_uint_t                      dyn_rec_threshold;\n } ngx_http_ssl_srv_conf_t;\n \n \ndiff --color -uNr a/src/http/ngx_http.c b/src/http/ngx_http.c\n--- a/src/http/ngx_http.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/ngx_http.c\t2023-03-12 11:07:31.418411950 +0800\n@@ -1178,6 +1178,7 @@\n ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,\n     ngx_http_listen_opt_t *lsopt)\n {\n+    int                         t;\n     in_port_t                   p;\n     ngx_uint_t                  i;\n     struct sockaddr            *sa;\n@@ -1196,11 +1197,13 @@\n \n     sa = lsopt->sockaddr;\n     p = ngx_inet_get_port(sa);\n+    t = lsopt->quic ? SOCK_DGRAM : SOCK_STREAM;\n \n     port = cmcf->ports->elts;\n     for (i = 0; i < cmcf->ports->nelts; i++) {\n \n-        if (p != port[i].port || sa->sa_family != port[i].family) {\n+        if (p != port[i].port || sa->sa_family != port[i].family\n+             || t != port[i].type) {\n             continue;\n         }\n \n@@ -1219,6 +1222,7 @@\n     port->family = sa->sa_family;\n     port->port = p;\n     port->addrs.elts = NULL;\n+    port->type = t;\n \n     return ngx_http_add_address(cf, cscf, port, lsopt);\n }\n@@ -1236,6 +1240,9 @@\n #if (NGX_HTTP_V2)\n     ngx_uint_t             http2;\n #endif\n+#if (NGX_HTTP_V3)\n+    ngx_uint_t             quic;\n+#endif\n \n     /*\n      * we cannot compare whole sockaddr struct's as kernel\n@@ -1271,6 +1278,9 @@\n #if (NGX_HTTP_V2)\n         http2 = lsopt->http2 || addr[i].opt.http2;\n #endif\n+#if (NGX_HTTP_V3)\n+        quic = lsopt->quic || addr[i].opt.quic;\n+#endif\n \n         if (lsopt->set) {\n \n@@ -1307,6 +1317,9 @@\n #if (NGX_HTTP_V2)\n         addr[i].opt.http2 = http2;\n #endif\n+#if (NGX_HTTP_V3)\n+        addr[i].opt.quic = quic;\n+#endif\n \n         return NGX_OK;\n     }\n@@ -1724,6 +1737,12 @@\n             break;\n         }\n \n+#if (NGX_HTTP_V3)\n+        if (addr[i].opt.quic) {\n+            ls->type = SOCK_DGRAM;\n+        }\n+#endif\n+\n         addr++;\n         last--;\n     }\n@@ -1805,6 +1824,12 @@\n     ls->reuseport = addr->opt.reuseport;\n #endif\n \n+#if (NGX_HTTP_V3)\n+    ls->quic = addr->opt.quic;\n+\n+    ls->wildcard = addr->opt.wildcard;\n+#endif\n+\n     return ls;\n }\n \n@@ -1838,6 +1863,9 @@\n         addrs[i].conf.http2 = addr[i].opt.http2;\n #endif\n         addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n+#if (NGX_HTTP_V3)\n+        addrs[i].conf.quic = addr[i].opt.quic;\n+#endif\n \n         if (addr[i].hash.buckets == NULL\n             && (addr[i].wc_head == NULL\n@@ -1903,6 +1931,9 @@\n         addrs6[i].conf.http2 = addr[i].opt.http2;\n #endif\n         addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n+#if (NGX_HTTP_V3)\n+        addrs6[i].conf.quic = addr[i].opt.quic;\n+#endif\n \n         if (addr[i].hash.buckets == NULL\n             && (addr[i].wc_head == NULL\ndiff --color -uNr a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c\n--- a/src/http/ngx_http_core_module.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/ngx_http_core_module.c\t2023-03-12 11:07:31.419411976 +0800\n@@ -4283,6 +4283,13 @@\n             continue;\n         }\n \n+#if (NGX_HTTP_V3)\n+        if (ngx_strcmp(value[n].data, \"quic\") == 0) {\n+            lsopt.quic = 1;\n+            continue;\n+        }\n+#endif\n+\n         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                            \"invalid parameter \\\"%V\\\"\", &value[n]);\n         return NGX_CONF_ERROR;\ndiff --color -uNr a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h\n--- a/src/http/ngx_http_core_module.h\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/ngx_http_core_module.h\t2023-03-12 11:07:31.419411976 +0800\n@@ -82,6 +82,7 @@\n     unsigned                   reuseport:1;\n     unsigned                   so_keepalive:2;\n     unsigned                   proxy_protocol:1;\n+    unsigned                   quic:1;\n \n     int                        backlog;\n     int                        rcvbuf;\n@@ -238,6 +239,7 @@\n     unsigned                   ssl:1;\n     unsigned                   http2:1;\n     unsigned                   proxy_protocol:1;\n+    unsigned                   quic:1;\n };\n \n \n@@ -268,6 +270,7 @@\n     ngx_int_t                  family;\n     in_port_t                  port;\n     ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */\n+    ngx_int_t                  type;\n } ngx_http_conf_port_t;\n \n \ndiff --color -uNr a/src/http/ngx_http.h b/src/http/ngx_http.h\n--- a/src/http/ngx_http.h\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/ngx_http.h\t2023-03-12 11:07:31.418411950 +0800\n@@ -20,6 +20,7 @@\n typedef struct ngx_http_log_ctx_s     ngx_http_log_ctx_t;\n typedef struct ngx_http_chunked_s     ngx_http_chunked_t;\n typedef struct ngx_http_v2_stream_s   ngx_http_v2_stream_t;\n+typedef struct ngx_http_v3_stream_s   ngx_http_v3_stream_t;\n \n typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,\n     ngx_table_elt_t *h, ngx_uint_t offset);\n@@ -38,6 +39,9 @@\n #if (NGX_HTTP_V2)\n #include <ngx_http_v2.h>\n #endif\n+#if (NGX_HTTP_V3)\n+#include <ngx_http_v3.h>\n+#endif\n #if (NGX_HTTP_CACHE)\n #include <ngx_http_cache.h>\n #endif\ndiff --color -uNr a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c\n--- a/src/http/ngx_http_request_body.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/ngx_http_request_body.c\t2023-03-12 11:07:31.421412029 +0800\n@@ -314,6 +314,12 @@\n                             ngx_del_timer(c->read);\n                         }\n \n+#if (NGX_HTTP_V3)\n+                        if (r->qstream) {\n+                            return NGX_AGAIN;\n+                        }\n+#endif\n+\n                         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                             return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                         }\n@@ -418,6 +424,12 @@\n             clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n             ngx_add_timer(c->read, clcf->client_body_timeout);\n \n+#if (NGX_HTTP_V3)\n+            if (r->qstream) {\n+                return NGX_AGAIN;\n+            }\n+#endif\n+\n             if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                 return NGX_HTTP_INTERNAL_SERVER_ERROR;\n             }\n@@ -625,6 +637,17 @@\n     }\n #endif\n \n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        r->qstream->skip_data = 1;\n+\n+        /* disable stream read to avoid pointless data events */\n+        ngx_http_v3_stop_stream_read(r->qstream, 0);\n+\n+        return NGX_OK;\n+    }\n+#endif\n+\n     if (ngx_http_test_expect(r) != NGX_OK) {\n         return NGX_HTTP_INTERNAL_SERVER_ERROR;\n     }\n@@ -921,6 +944,9 @@\n #if (NGX_HTTP_V2)\n         || r->stream != NULL\n #endif\n+#if (NGX_HTTP_V3)\n+        || r->qstream != NULL\n+#endif\n        )\n     {\n         return NGX_OK;\n@@ -960,6 +986,13 @@\n static ngx_int_t\n ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n {\n+\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        return ngx_http_v3_request_body_filter(r, in);\n+    }\n+#endif\n+\n     if (r->headers_in.chunked) {\n         return ngx_http_request_body_chunked_filter(r, in);\n \ndiff --color -uNr a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c\n--- a/src/http/ngx_http_request.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/ngx_http_request.c\t2023-03-12 11:07:46.631813037 +0800\n@@ -62,6 +62,10 @@\n static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);\n #endif\n \n+#if (NGX_HTTP_V3)\n+static void ngx_http_quic_handshake(ngx_event_t *rev);\n+#endif\n+\n \n static char *ngx_http_client_errors[] = {\n \n@@ -348,6 +352,20 @@\n         c->log->action = \"reading PROXY protocol\";\n     }\n \n+#if (NGX_HTTP_V3)\n+    if (hc->addr_conf->quic) {\n+        hc->quic = 1;\n+        c->log->action = \"QUIC handshaking\";\n+\n+        /* We already have a UDP packet in the connection buffer, so we don't\n+         * need to wait for another read event to kick-off the handshake. */\n+        cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);\n+        ngx_add_timer(rev, cscf->client_header_timeout);\n+        ngx_http_quic_handshake(rev);\n+        return;\n+    }\n+#endif\n+\n     if (rev->ready) {\n         /* the deferred accept(), iocp */\n \n@@ -803,8 +821,9 @@\n \n         c->ssl->no_wait_shutdown = 1;\n \n-#if (NGX_HTTP_V2                                                              \\\n-     && defined TLSEXT_TYPE_application_layer_protocol_negotiation)\n+#if ((NGX_HTTP_V2 || NGX_HTTP_V3)                                             \\\n+     && (defined TLSEXT_TYPE_application_layer_protocol_negotiation           \\\n+         || defined TLSEXT_TYPE_next_proto_neg))\n         {\n         unsigned int            len;\n         const unsigned char    *data;\n@@ -812,15 +831,33 @@\n \n         hc = c->data;\n \n-        if (hc->addr_conf->http2) {\n+        if (hc->addr_conf->http2 || hc->addr_conf->quic) {\n \n             SSL_get0_alpn_selected(c->ssl->connection, &data, &len);\n \n+        }\n+\n+#if (NGX_HTTP_V2)\n+        if (hc->addr_conf->http2) {\n             if (len == 2 && data[0] == 'h' && data[1] == '2') {\n                 ngx_http_v2_init(c->read);\n                 return;\n             }\n         }\n+#endif\n+\n+#if (NGX_HTTP_V3)\n+        if (hc->addr_conf->quic) {\n+            if (len >= 2 && data[0] == 'h' && data[1] == '3') {\n+                ngx_http_v3_init(c->read);\n+                return;\n+            }\n+\n+            ngx_http_close_connection(c);\n+            return;\n+        }\n+#endif\n+\n         }\n #endif\n \n@@ -1045,6 +1082,70 @@\n \n #endif\n \n+#if (NGX_HTTP_V3)\n+\n+static void\n+ngx_http_quic_handshake(ngx_event_t *rev)\n+{\n+    ngx_int_t                  rc;\n+    ngx_connection_t          *c;\n+    ngx_http_connection_t     *hc;\n+    ngx_http_v3_srv_conf_t    *qscf;\n+    ngx_http_ssl_srv_conf_t   *sscf;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    c = rev->data;\n+    hc = c->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,\n+                   \"http check quic handshake\");\n+\n+    if (rev->timedout) {\n+        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    if (c->close) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, \"https quic handshake\");\n+\n+    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,\n+                                        ngx_http_ssl_module);\n+\n+    if (ngx_ssl_create_connection(&sscf->ssl, c, 0) != NGX_OK) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    qscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);\n+\n+    if (ngx_quic_create_connection(&qscf->quic, c) != NGX_OK) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    rc = ngx_quic_handshake(c);\n+\n+    if (rc == NGX_AGAIN) {\n+\n+        if (!rev->timer_set) {\n+            cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);\n+            ngx_add_timer(rev, cscf->client_header_timeout);\n+        }\n+\n+        c->ssl->handler = ngx_http_ssl_handshake_handler;\n+        return;\n+    }\n+\n+    ngx_http_ssl_handshake_handler(c);\n+}\n+\n+#endif\n+\n \n static void\n ngx_http_process_request_line(ngx_event_t *rev)\n@@ -2710,6 +2811,13 @@\n     }\n #endif\n \n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        ngx_http_close_request(r, 0);\n+        return;\n+    }\n+#endif\n+\n     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n \n     if (r->main->count != 1) {\n@@ -2924,6 +3032,19 @@\n \n #endif\n \n+#if (NGX_HTTP_V3)\n+\n+    if (r->qstream) {\n+        if (c->error) {\n+            err = 0;\n+            goto closed;\n+        }\n+\n+        return;\n+    }\n+\n+#endif\n+\n #if (NGX_HAVE_KQUEUE)\n \n     if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n@@ -3616,7 +3737,15 @@\n     }\n #endif\n \n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        ngx_http_v3_close_stream(r->qstream, rc);\n+        return;\n+    }\n+#endif\n+\n     ngx_http_free_request(r, rc);\n+\n     ngx_http_close_connection(c);\n }\n \n@@ -3737,6 +3866,17 @@\n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                    \"close http connection: %d\", c->fd);\n \n+#if (NGX_HTTP_V3)\n+\n+    if (c->quic) {\n+        if (ngx_quic_shutdown(c) == NGX_AGAIN) {\n+            c->quic->handler = ngx_http_close_connection;\n+            return;\n+        }\n+    }\n+\n+#endif\n+\n #if (NGX_HTTP_SSL)\n \n     if (c->ssl) {\ndiff --color -uNr a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h\n--- a/src/http/ngx_http_request.h\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/ngx_http_request.h\t2023-03-12 11:07:31.421412029 +0800\n@@ -24,6 +24,7 @@\n #define NGX_HTTP_VERSION_10                1000\n #define NGX_HTTP_VERSION_11                1001\n #define NGX_HTTP_VERSION_20                2000\n+#define NGX_HTTP_VERSION_3                 3000\n \n #define NGX_HTTP_UNKNOWN                   0x00000001\n #define NGX_HTTP_GET                       0x00000002\n@@ -329,6 +330,7 @@\n     ngx_chain_t                      *free;\n \n     unsigned                          ssl:1;\n+    unsigned                          quic:1;\n     unsigned                          proxy_protocol:1;\n } ngx_http_connection_t;\n \n@@ -451,6 +453,7 @@\n \n     ngx_http_connection_t            *http_connection;\n     ngx_http_v2_stream_t             *stream;\n+    ngx_http_v3_stream_t             *qstream;\n \n     ngx_http_log_handler_pt           log_handler;\n \ndiff --color -uNr a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c\n--- a/src/http/ngx_http_upstream.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/ngx_http_upstream.c\t2023-03-12 11:07:31.422412056 +0800\n@@ -521,6 +521,13 @@\n     }\n #endif\n \n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        ngx_http_upstream_init_request(r);\n+        return;\n+    }\n+#endif\n+\n     if (c->read->timer_set) {\n         ngx_del_timer(c->read);\n     }\n@@ -1353,6 +1360,12 @@\n         return;\n     }\n #endif\n+\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        return;\n+    }\n+#endif\n \n #if (NGX_HAVE_KQUEUE)\n \ndiff --color -uNr a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c\n--- a/src/http/v2/ngx_http_v2.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.c\t2023-03-12 11:07:39.515625425 +0800\n@@ -274,6 +274,8 @@\n \n     h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n \n+    h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE;\n+\n     h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n \n     h2c->concurrent_pushes = h2scf->concurrent_pushes;\n@@ -2283,6 +2285,14 @@\n         case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:\n \n             h2c->table_update = 1;\n+\n+            if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+                h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE;\n+            } else {\n+                h2c->max_hpack_table_size = value;\n+            }\n+\n+            h2c->indicate_resize = 1;\n             break;\n \n         default:\ndiff --color -uNr a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c\n--- a/src/http/v2/ngx_http_v2_encode.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_encode.c\t2023-03-12 11:07:39.515625425 +0800\n@@ -10,7 +10,7 @@\n #include <ngx_http.h>\n \n \n-static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n+u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n     ngx_uint_t value);\n \n \n@@ -40,7 +40,7 @@\n }\n \n \n-static u_char *\n+u_char *\n ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)\n {\n     if (value < prefix) {\ndiff --color -uNr a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c\n--- a/src/http/v2/ngx_http_v2_filter_module.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_filter_module.c\t2023-03-12 11:07:39.516625451 +0800\n@@ -23,10 +23,53 @@\n #define ngx_http_v2_literal_size(h)                                           \\\n     (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)\n \n+#define ngx_http_v2_indexed(i)      (128 + (i))\n+#define ngx_http_v2_inc_indexed(i)  (64 + (i))\n+\n+#define NGX_HTTP_V2_ENCODE_RAW            0\n+#define NGX_HTTP_V2_ENCODE_HUFF           0x80\n+\n+#define NGX_HTTP_V2_AUTHORITY_INDEX       1\n+#define NGX_HTTP_V2_METHOD_GET_INDEX      2\n+#define NGX_HTTP_V2_PATH_INDEX            4\n+\n+#define NGX_HTTP_V2_SCHEME_HTTP_INDEX     6\n+#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX    7\n+\n+#define NGX_HTTP_V2_STATUS_INDEX          8\n+#define NGX_HTTP_V2_STATUS_200_INDEX      8\n+#define NGX_HTTP_V2_STATUS_204_INDEX      9\n+#define NGX_HTTP_V2_STATUS_206_INDEX      10\n+#define NGX_HTTP_V2_STATUS_304_INDEX      11\n+#define NGX_HTTP_V2_STATUS_400_INDEX      12\n+#define NGX_HTTP_V2_STATUS_404_INDEX      13\n+#define NGX_HTTP_V2_STATUS_500_INDEX      14\n+\n+#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16\n+#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17\n+#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX  28\n+#define NGX_HTTP_V2_CONTENT_TYPE_INDEX    31\n+#define NGX_HTTP_V2_DATE_INDEX            33\n+#define NGX_HTTP_V2_LAST_MODIFIED_INDEX   44\n+#define NGX_HTTP_V2_LOCATION_INDEX        46\n+#define NGX_HTTP_V2_SERVER_INDEX          54\n+#define NGX_HTTP_V2_USER_AGENT_INDEX      58\n+#define NGX_HTTP_V2_VARY_INDEX            59\n \n #define NGX_HTTP_V2_NO_TRAILERS           (ngx_http_v2_out_frame_t *) -1\n \n \n+static const struct {\n+    u_char        *name;\n+    u_char const   len;\n+} push_header[] = {\n+    { (u_char*)\":authority\"      , 10 },\n+    { (u_char*)\"accept-encoding\" , 15 },\n+    { (u_char*)\"accept-language\" , 15 },\n+    { (u_char*)\"user-agent\"      , 10 }\n+};\n+\n+\n typedef struct {\n     ngx_str_t      name;\n     u_char         index;\n@@ -155,11 +198,9 @@\n #endif\n \n     static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);\n-    static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];\n \n     static size_t nginx_ver_build_len =\n                                   ngx_http_v2_literal_size(NGINX_VER_BUILD);\n-    static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];\n \n     stream = r->stream;\n \n@@ -435,7 +476,7 @@\n     }\n \n     tmp = ngx_palloc(r->pool, tmp_len);\n-    pos = ngx_pnalloc(r->pool, len);\n+    pos = ngx_pnalloc(r->pool, len + 15 + 1);\n \n     if (pos == NULL || tmp == NULL) {\n         return NGX_ERROR;\n@@ -443,11 +484,16 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n     }\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n@@ -458,67 +504,28 @@\n         *pos++ = status;\n \n     } else {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);\n-        *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;\n-        pos = ngx_sprintf(pos, \"%03ui\", r->headers_out.status);\n+        ngx_sprintf(pos + 8, \"%O3ui\", r->headers_out.status);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\":status\",\n+                                       sizeof(\":status\") - 1, pos + 8, 3, tmp);\n     }\n \n     if (r->headers_out.server == NULL) {\n-\n         if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER);\n-\n-        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER_BUILD);\n-\n-        } else {\n-            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: nginx\\\"\");\n-        }\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);\n-\n-        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            if (nginx_ver[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,\n-                                            sizeof(NGINX_VER) - 1, tmp);\n-                nginx_ver_len = p - nginx_ver;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER);\n \n         } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            if (nginx_ver_build[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver_build,\n-                                            (u_char *) NGINX_VER_BUILD,\n-                                            sizeof(NGINX_VER_BUILD) - 1, tmp);\n-                nginx_ver_build_len = p - nginx_ver_build;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER_BUILD);\n \n         } else {\n-            pos = ngx_cpymem(pos, nginx, sizeof(nginx));\n+            pos = ngx_http_v2_write_header_str(\"server\", \"nginx\");\n         }\n     }\n \n     if (r->headers_out.date == NULL) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"date: %V\\\"\",\n-                       &ngx_cached_http_time);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);\n-        pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,\n-                                      ngx_cached_http_time.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"date\", ngx_cached_http_time);\n     }\n \n     if (r->headers_out.content_type.len) {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);\n-\n         if (r->headers_out.content_type_len == r->headers_out.content_type.len\n             && r->headers_out.charset.len)\n         {\n@@ -544,64 +551,36 @@\n             r->headers_out.content_type.data = p - len;\n         }\n \n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-type: %V\\\"\",\n-                       &r->headers_out.content_type);\n-\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,\n-                                      r->headers_out.content_type.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"content-type\",\n+                                           r->headers_out.content_type);\n     }\n \n     if (r->headers_out.content_length == NULL\n         && r->headers_out.content_length_n >= 0)\n     {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-length: %O\\\"\",\n-                       r->headers_out.content_length_n);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);\n-\n-        p = pos;\n-        pos = ngx_sprintf(pos + 1, \"%O\", r->headers_out.content_length_n);\n-        *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);\n+        p = ngx_sprintf(pos + 15, \"%O\", r->headers_out.content_length_n);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"content-length\",\n+                                       sizeof(\"content-length\") - 1, pos + 15,\n+                                       p - (pos + 15), tmp);\n     }\n \n     if (r->headers_out.last_modified == NULL\n         && r->headers_out.last_modified_time != -1)\n     {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);\n-\n-        ngx_http_time(pos, r->headers_out.last_modified_time);\n+        ngx_http_time(pos + 14, r->headers_out.last_modified_time);\n         len = sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1;\n-\n-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"last-modified: %*s\\\"\",\n-                       len, pos);\n-\n-        /*\n-         * Date will always be encoded using huffman in the temporary buffer,\n-         * so it's safe here to use src and dst pointing to the same address.\n-         */\n-        pos = ngx_http_v2_write_value(pos, pos, len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"last-modified\",\n+                                       sizeof(\"last-modified\") - 1, pos + 14,\n+                                       len, tmp);\n     }\n \n     if (r->headers_out.location && r->headers_out.location->value.len) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"location: %V\\\"\",\n-                       &r->headers_out.location->value);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,\n-                                      r->headers_out.location->value.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"location\", r->headers_out.location->value);\n     }\n \n #if (NGX_HTTP_GZIP)\n     if (r->gzip_vary) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"vary: Accept-Encoding\\\"\");\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);\n-        pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));\n+        pos = ngx_http_v2_write_header_str(\"vary\", \"Accept-Encoding\");\n     }\n #endif\n \n@@ -624,23 +603,10 @@\n             continue;\n         }\n \n-#if (NGX_DEBUG)\n-        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n-            ngx_strlow(tmp, header[i].key.data, header[i].key.len);\n-\n-            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"%*s: %V\\\"\",\n-                           header[i].key.len, tmp, &header[i].value);\n-        }\n-#endif\n-\n-        *pos++ = 0;\n-\n-        pos = ngx_http_v2_write_name(pos, header[i].key.data,\n-                                     header[i].key.len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data,\n+                                       header[i].key.len, header[i].value.data,\n+                                       header[i].value.len, tmp);\n \n-        pos = ngx_http_v2_write_value(pos, header[i].value.data,\n-                                      header[i].value.len, tmp);\n     }\n \n     fin = r->header_only\n@@ -997,6 +963,7 @@\n \n     for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n         len += binary[i].len;\n+        len += push_header[i].len + 1;\n     }\n \n     pos = ngx_pnalloc(r->pool, len);\n@@ -1006,12 +973,17 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n-    }\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n+     }\n \n     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":method: GET\\\"\");\n@@ -1021,8 +993,7 @@\n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":path: %V\\\"\", path);\n \n-    *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n-    pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);\n+    pos = ngx_http_v2_write_header_pot(\":path\", path);\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":scheme: %V\\\"\", &r->schema);\n@@ -1047,11 +1018,15 @@\n             continue;\n         }\n \n+        value = &(*h)->value;\n+\n         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                        \"http2 push header: \\\"%V: %V\\\"\",\n                        &ph[i].name, &(*h)->value);\n \n-        pos = ngx_cpymem(pos, binary[i].data, binary[i].len);\n+        pos = ngx_http_v2_write_header(h2c, pos,\n+                  push_header[i].name, push_header[i].len, value->data, value->len,\n+                  tmp);\n     }\n \n     frame = ngx_http_v2_create_push_frame(r, start, pos);\ndiff --color -uNr a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h\n--- a/src/http/v2/ngx_http_v2.h\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.h\t2023-03-12 11:07:39.516625451 +0800\n@@ -51,6 +51,14 @@\n #define NGX_HTTP_V2_MAX_WINDOW           ((1U << 31) - 1)\n #define NGX_HTTP_V2_DEFAULT_WINDOW       65535\n \n+#define HPACK_ENC_HTABLE_SZ              128 /* better to keep a PoT < 64k */\n+#define HPACK_ENC_HTABLE_ENTRIES         ((HPACK_ENC_HTABLE_SZ * 100) / 128)\n+#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ     10  /* 10 is sufficient for most */\n+#define HPACK_ENC_MAX_ENTRY              512 /* longest header size to match */\n+\n+#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE     4096\n+#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE         16384 /* < 64k */\n+\n #define NGX_HTTP_V2_DEFAULT_WEIGHT       16\n \n \n@@ -114,6 +122,46 @@\n } ngx_http_v2_hpack_t;\n \n \n+#if (NGX_HTTP_V2_HPACK_ENC)\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen, vlen;\n+    uint16_t                         size;\n+    uint16_t                         next;\n+} ngx_http_v2_hpack_enc_entry_t;\n+\n+\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen;\n+} ngx_http_v2_hpack_name_entry_t;\n+\n+\n+typedef struct {\n+    size_t                           size;    /* size as defined in RFC 7541 */\n+    uint32_t                         top;     /* the last entry */\n+    uint32_t                         pos;\n+    uint16_t                         n_elems; /* number of elements */\n+    uint16_t                         base;    /* index of the oldest entry */\n+    uint16_t                         last;    /* index of the newest entry */\n+\n+    /* hash table for dynamic entries, instead using a generic hash table,\n+       which would be too slow to process a significant amount of headers,\n+       this table is not determenistic, and might ocasionally fail to insert\n+       a value, at the cost of slightly worse compression, but significantly\n+       faster performance */\n+    ngx_http_v2_hpack_enc_entry_t    htable[HPACK_ENC_HTABLE_SZ];\n+    ngx_http_v2_hpack_name_entry_t   heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ];\n+    u_char                           storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE +\n+                                             HPACK_ENC_MAX_ENTRY];\n+} ngx_http_v2_hpack_enc_t;\n+#endif\n+\n+\n struct ngx_http_v2_connection_s {\n     ngx_connection_t                *connection;\n     ngx_http_connection_t           *http_connection;\n@@ -135,6 +183,8 @@\n \n     size_t                           frame_size;\n \n+    size_t                           max_hpack_table_size;\n+\n     ngx_queue_t                      waiting;\n \n     ngx_http_v2_state_t              state;\n@@ -164,6 +214,11 @@\n     unsigned                         blocked:1;\n     unsigned                         goaway:1;\n     unsigned                         push_disabled:1;\n+    unsigned                         indicate_resize:1;\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+    ngx_http_v2_hpack_enc_t          hpack_enc;\n+#endif\n };\n \n \n@@ -207,6 +262,8 @@\n \n     ngx_array_t                     *cookies;\n \n+    size_t                           header_limit;\n+\n     ngx_pool_t                      *pool;\n \n     unsigned                         waiting:1;\n@@ -413,4 +470,35 @@\n     u_char *tmp, ngx_uint_t lower);\n \n \n+u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,\n+    u_char *tmp, ngx_uint_t lower);\n+\n+u_char *\n+ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value);\n+\n+#define ngx_http_v2_write_name(dst, src, len, tmp)                            \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 1)\n+#define ngx_http_v2_write_value(dst, src, len, tmp)                           \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 0)\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+    u_char *key, size_t key_len, u_char *value, size_t value_len,\n+    u_char *tmp);\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c);\n+\n+#define ngx_http_v2_write_header_str(key, value)                        \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    (u_char *) value, sizeof(value) - 1, tmp);\n+\n+#define ngx_http_v2_write_header_tbl(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val.data, val.len, tmp);\n+\n+#define ngx_http_v2_write_header_pot(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val->data, val->len, tmp);\n+\n #endif /* _NGX_HTTP_V2_H_INCLUDED_ */\ndiff --color -uNr a/src/http/v2/ngx_http_v2_table.c b/src/http/v2/ngx_http_v2_table.c\n--- a/src/http/v2/ngx_http_v2_table.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_table.c\t2023-03-12 11:07:39.517625477 +0800\n@@ -361,3 +361,434 @@\n \n     return NGX_OK;\n }\n+\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len);\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len);\n+\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c)\n+{\n+    ngx_http_v2_hpack_enc_entry_t  *table;\n+    uint64_t                        idx;\n+\n+    table = h2c->hpack_enc.htable;\n+\n+    while (h2c->hpack_enc.size > h2c->max_hpack_table_size) {\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+}\n+\n+\n+/* checks if a header is in the hpack table - if so returns the table entry,\n+   otherwise encodes and inserts into the table and returns 0,\n+   if failed to insert into table, returns -1 */\n+static ngx_int_t\n+ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c,\n+    size_t key_len, size_t val_len, uint8_t *key, uint8_t *val,\n+    ngx_int_t *header_idx)\n+{\n+    uint64_t  hash_val, key_hash, idx, lru;\n+    int       i;\n+    size_t    size = key_len + val_len + 32;\n+    uint8_t  *storage = h2c->hpack_enc.storage;\n+\n+    ngx_http_v2_hpack_enc_entry_t   *table;\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+\n+    *header_idx = NGX_ERROR;\n+    /* step 1: compute the hash value of header */\n+    if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) {\n+        return NGX_ERROR;\n+    }\n+\n+    key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234);\n+    hash_val = ngx_murmur_hash2_64(val, val_len, key_hash);\n+\n+    if (hash_val == 0) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* step 2: check if full header in the table */\n+    idx = hash_val;\n+    i = -1;\n+    while (idx) {\n+         /* at most 8 locations are checked, but most will be done in 1 or 2 */\n+        table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ];\n+        if (table->hash_val == hash_val\n+            && table->klen == key_len\n+            && table->vlen == val_len\n+            && ngx_memcmp(key, storage + table->pos, key_len) == 0\n+            && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0)\n+        {\n+            return (h2c->hpack_enc.top - table->index) + 61;\n+        }\n+\n+        if (table->hash_val == 0 && i == -1) {\n+            i = idx % HPACK_ENC_HTABLE_SZ;\n+            break;\n+        }\n+\n+        idx >>= 8;\n+    }\n+\n+    /* step 3: check if key is in one of the tables */\n+    *header_idx = hpack_get_static_index(h2c, key, key_len);\n+\n+    if (i == -1) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (*header_idx == NGX_ERROR) {\n+        *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len);\n+    }\n+\n+    /* step 4: store the new entry */\n+    table =  h2c->hpack_enc.htable;\n+\n+    if (h2c->hpack_enc.top == 0xffffffff) {\n+        /* just to be on the safe side, avoid overflow */\n+        ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t));\n+    }\n+\n+    while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size)\n+           || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) {\n+        /* make space for the new entry first */\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+\n+    table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val,\n+                                               .index = h2c->hpack_enc.top,\n+                                               .pos = h2c->hpack_enc.pos,\n+                                               .klen = key_len,\n+                                               .vlen = val_len,\n+                                               .size = size,\n+                                               .next = 0};\n+\n+    table[h2c->hpack_enc.last].next = i;\n+    if (h2c->hpack_enc.n_elems == 0) {\n+        h2c->hpack_enc.base = i;\n+    }\n+\n+    h2c->hpack_enc.last = i;\n+    h2c->hpack_enc.top++;\n+    h2c->hpack_enc.size += size;\n+    h2c->hpack_enc.n_elems++;\n+\n+    /* update header name lookup */\n+    if (*header_idx == NGX_ERROR ) {\n+        lru = h2c->hpack_enc.top;\n+\n+        for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+\n+            name = &h2c->hpack_enc.heads[i];\n+\n+            if ( name->hash_val == 0 || (name->hash_val == key_hash\n+                && ngx_memcmp(storage + name->pos, key, key_len) == 0) )\n+            {\n+                name->hash_val = key_hash;\n+                name->pos = h2c->hpack_enc.pos;\n+                name->index = h2c->hpack_enc.top - 1;\n+                break;\n+            }\n+\n+            if (lru > name->index) {\n+                lru = name->index;\n+                idx = i;\n+            }\n+        }\n+\n+        if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) {\n+            name = &h2c->hpack_enc.heads[idx];\n+            name->hash_val = hash_val;\n+            name->pos = h2c->hpack_enc.pos;\n+            name->index = h2c->hpack_enc.top - 1;\n+        }\n+    }\n+\n+    ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len);\n+    ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len);\n+\n+    h2c->hpack_enc.pos += size;\n+    if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+        h2c->hpack_enc.pos = 0;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_int_t idx, header_idx;\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    /* attempt to find the value in the dynamic table */\n+    idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value,\n+                                           &header_idx);\n+\n+    if (idx > 0) {\n+        /* positive index indicates success */\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                       \"http2 hpack encode: Indexed Header Field: %ud\", idx);\n+\n+        *pos = 128;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx);\n+\n+    } else {\n+\n+        if (header_idx == NGX_ERROR) { /* if key is not present */\n+\n+            if (idx == NGX_ERROR) {    /* if header was not added */\n+                *pos++ = 0;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — New Name\");\n+            } else {                   /* if header was added */\n+                *pos++ = 64;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — New Name\");\n+            }\n+\n+            pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+\n+        } else {                       /* if key is present */\n+\n+            if (idx == NGX_ERROR) {\n+                *pos = 0;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — Indexed Name: %ud\", header_idx);\n+            } else {\n+                *pos = 64;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — Indexed Name: %ud\", header_idx);\n+            }\n+        }\n+\n+        pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+    }\n+\n+    return pos;\n+}\n+\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len)\n+{\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+    int                              i;\n+\n+    for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+        name = &h2c->hpack_enc.heads[i];\n+\n+        if (name->hash_val == key_hash\n+            && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0)\n+        {\n+            if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) {\n+                return (h2c->hpack_enc.top - name->index) + 61;\n+            }\n+            break;\n+        }\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+\n+/* decide if a given header is present in the static dictionary, this could be\n+   done in several ways, but it seems the fastest one is \"exhaustive\" search */\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len)\n+{\n+    /* the static dictionary of response only headers,\n+       although response headers can be put by origin,\n+       that would be rare */\n+    static const struct {\n+        u_char         len;\n+        const u_char   val[28];\n+        u_char         idx;\n+    } server_headers[] = {\n+        { 3, \"age\",                         21},//0\n+        { 3, \"via\",                         60},\n+        { 4, \"date\",                        33},//2\n+        { 4, \"etag\",                        34},\n+        { 4, \"link\",                        45},\n+        { 4, \"vary\",                        59},\n+        { 5, \"allow\",                       22},//6\n+        { 6, \"server\",                      54},//7\n+        { 7, \"expires\",                     36},//8\n+        { 7, \"refresh\",                     52},\n+        { 8, \"location\",                    46},//10\n+        {10, \"set-cookie\",                  55},//11\n+        {11, \"retry-after\",                 53},//12\n+        {12, \"content-type\",                31},//13\n+        {13, \"content-range\",               30},//14\n+        {13, \"accept-ranges\",               18},\n+        {13, \"cache-control\",               24},\n+        {13, \"last-modified\",               44},\n+        {14, \"content-length\",              28},//18\n+        {16, \"content-encoding\",            26},//19\n+        {16, \"content-language\",            27},\n+        {16, \"content-location\",            29},\n+        {16, \"www-authenticate\",            61},\n+        {17, \"transfer-encoding\",           57},//23\n+        {18, \"proxy-authenticate\",          48},//24\n+        {19, \"content-disposition\",         25},//25\n+        {25, \"strict-transport-security\",   56},//26\n+        {27, \"access-control-allow-origin\", 20},//27\n+        {99, \"\",                            99},\n+    }, *header;\n+\n+    /* for a given length, where to start the search\n+       since minimal length is 3, the table has a -3\n+       offset */\n+    static const int8_t start_at[] = {\n+        [3-3]  = 0,\n+        [4-3]  = 2,\n+        [5-3]  = 6,\n+        [6-3]  = 7,\n+        [7-3]  = 8,\n+        [8-3]  = 10,\n+        [9-3]  = -1,\n+        [10-3] = 11,\n+        [11-3] = 12,\n+        [12-3] = 13,\n+        [13-3] = 14,\n+        [14-3] = 18,\n+        [15-3] = -1,\n+        [16-3] = 19,\n+        [17-3] = 23,\n+        [18-3] = 24,\n+        [19-3] = 25,\n+        [20-3] = -1,\n+        [21-3] = -1,\n+        [22-3] = -1,\n+        [23-3] = -1,\n+        [24-3] = -1,\n+        [25-3] = 26,\n+        [26-3] = -1,\n+        [27-3] = 27,\n+    };\n+\n+    uint64_t pref;\n+    size_t   save_len = len, i;\n+    int8_t   start;\n+\n+    /* early exit for out of bounds lengths */\n+    if (len < 3 || len > 27) {\n+        return NGX_ERROR;\n+    }\n+\n+    start = start_at[len - 3];\n+    if (start == -1) {\n+        /* exit for non existent lengths */\n+        return NGX_ERROR;\n+    }\n+\n+    header = &server_headers[start_at[len - 3]];\n+\n+    /* load first 8 bytes of key, for fast comparison */\n+    if (len < 8) {\n+        pref = 0;\n+        if (len >= 4) {\n+            pref = *(uint32_t *)(val + len - 4) | 0x20202020;\n+            len -= 4;\n+        }\n+        while (len > 0) { /* 3 iterations at most */\n+            pref = (pref << 8) ^ (val[len - 1] | 0x20);\n+            len--;\n+        }\n+    } else {\n+        pref = *(uint64_t *)val | 0x2020202020202020;\n+        len -= 8;\n+    }\n+\n+    /* iterate over headers with the right length */\n+    while (header->len == save_len) {\n+        /* quickly compare the first 8 bytes, most tests will end here */\n+        if (pref != *(uint64_t *) header->val) {\n+            header++;\n+            continue;\n+        }\n+\n+        if (len == 0) {\n+            /* len == 0, indicates prefix held the entire key */\n+            return header->idx;\n+        }\n+        /* for longer keys compare the rest */\n+        i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */\n+\n+        while (i + 8 <= save_len) { /* 3 iterations at most */\n+            if ( *(uint64_t *)&header->val[i]\n+                 != (*(uint64_t *) &val[i]| 0x2020202020202020) )\n+            {\n+                header++;\n+                i = 0;\n+                break;\n+            }\n+            i += 8;\n+        }\n+\n+        if (i == 0) {\n+            continue;\n+        }\n+\n+        /* found the corresponding entry in the static dictionary */\n+        return header->idx;\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+#else\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    *pos++ = 64;\n+    pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+    pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+\n+    return pos;\n+}\n+\n+#endif\ndiff --color -uNr a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c\n--- a/src/http/v3/ngx_http_v3.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3.c\t2023-03-12 11:07:31.423412082 +0800\n@@ -0,0 +1,2230 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+\n+typedef struct {\n+    ngx_str_t           name;\n+    ngx_uint_t          offset;\n+    ngx_uint_t          hash;\n+    ngx_http_header_t  *hh;\n+} ngx_http_v3_parse_header_t;\n+\n+\n+/* errors */\n+#define NGX_HTTP_V3_NO_ERROR                     0x0100\n+#define NGX_HTTP_V3_PROTOCOL_ERROR               0x0101\n+#define NGX_HTTP_V3_INTERNAL_ERROR               0x0102\n+\n+\n+static void ngx_http_v3_handler(ngx_connection_t *c);\n+\n+static void ngx_http_v3_idle_handler(ngx_connection_t *c);\n+\n+static void ngx_http_v3_handle_connection(ngx_http_v3_connection_t *h3c);\n+\n+static ngx_http_v3_stream_t *ngx_http_v3_stream_lookup(\n+    ngx_http_v3_connection_t *h3c, ngx_uint_t stream_id);\n+static ngx_http_v3_stream_t *ngx_http_v3_create_stream(\n+    ngx_http_v3_connection_t *h3c);\n+static void ngx_http_v3_close_stream_handler(ngx_event_t *ev);\n+\n+static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r,\n+    ngx_http_v3_header_t *header);\n+static ngx_int_t ngx_http_v3_pseudo_header(ngx_http_request_t *r,\n+    ngx_http_v3_header_t *header);\n+static ngx_int_t ngx_http_v3_parse_path(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_method(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_scheme(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_authority(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r,\n+    ngx_http_v3_parse_header_t *header, ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_cookie(ngx_http_request_t *r,\n+    ngx_http_v3_header_t *header);\n+static ngx_int_t ngx_http_v3_construct_cookie_header(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_v3_construct_request_line(ngx_http_request_t *r);\n+\n+static void ngx_http_v3_run_request(ngx_http_request_t *r);\n+\n+static ssize_t ngx_http_v3_recv_body(ngx_connection_t *c, u_char *buf,\n+    size_t size);\n+static ngx_chain_t *ngx_http_v3_send_chain(ngx_connection_t *fc,\n+    ngx_chain_t *in, off_t limit);\n+\n+static void ngx_http_v3_finalize_connection(ngx_http_v3_connection_t *h3c,\n+    ngx_uint_t status);\n+\n+static void ngx_http_v3_pool_cleanup(void *data);\n+\n+\n+static ngx_http_v3_parse_header_t  ngx_http_v3_parse_headers[] = {\n+    { ngx_string(\"host\"),\n+      offsetof(ngx_http_headers_in_t, host), 0, NULL },\n+\n+    { ngx_string(\"accept-encoding\"),\n+      offsetof(ngx_http_headers_in_t, accept_encoding), 0, NULL },\n+\n+    { ngx_string(\"accept-language\"),\n+      offsetof(ngx_http_headers_in_t, accept_language), 0, NULL },\n+\n+    { ngx_string(\"user-agent\"),\n+      offsetof(ngx_http_headers_in_t, user_agent), 0, NULL },\n+\n+    { ngx_null_string, 0, 0, NULL }\n+};\n+\n+\n+void\n+ngx_http_v3_init(ngx_event_t *rev)\n+{\n+    ngx_connection_t          *c;\n+    ngx_pool_cleanup_t        *cln;\n+    ngx_http_connection_t     *hc;\n+    ngx_http_v3_srv_conf_t    *h3scf;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    c = rev->data;\n+    hc = c->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"init http3 connection\");\n+\n+    c->log->action = \"processing HTTP/3 connection\";\n+\n+    h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t));\n+    if (h3c == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);\n+\n+    h3c->h3 = quiche_h3_conn_new_with_transport(c->quic->conn, h3scf->http3);\n+    if (h3c->h3 == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    h3c->http_connection = hc;\n+\n+    h3c->connection = c;\n+\n+    h3c->pool = c->pool;\n+\n+    c->data = h3c;\n+\n+    c->quic->handler = ngx_http_v3_handler;\n+\n+    cln = ngx_pool_cleanup_add(c->pool, 0);\n+    if (cln == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    cln->handler = ngx_http_v3_pool_cleanup;\n+    cln->data = h3c;\n+\n+    ngx_rbtree_init(&h3c->streams, &h3c->streams_sentinel,\n+                    ngx_rbtree_insert_value);\n+}\n+\n+\n+static int\n+ngx_http_v3_for_each_header(uint8_t *name, size_t name_len,\n+    uint8_t *value, size_t value_len, void *argp)\n+{\n+    ngx_int_t                   rc;\n+    ngx_table_elt_t            *h;\n+    ngx_http_header_t          *hh;\n+    ngx_http_request_t         *r;\n+    ngx_http_v3_header_t        header;\n+    ngx_http_core_srv_conf_t   *cscf;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    static ngx_str_t cookie = ngx_string(\"cookie\");\n+\n+    r = argp;\n+\n+    /* Duplicate the header name because we don't own it. */\n+    header.name.data = ngx_pnalloc(r->pool, name_len);\n+    if (header.name.data == NULL) {\n+        return NGX_ERROR;\n+    }\n+    header.name.len = name_len;\n+\n+    ngx_memcpy(header.name.data, name, name_len);\n+\n+    /* Duplicate the header value because we don't own it. Some of the\n+     * functions that process headers require a NULL-terminated string,\n+     * so allocate enough memory for that. */\n+    header.value.data = ngx_pcalloc(r->pool, value_len + 1);\n+    if (header.value.data == NULL) {\n+        return NGX_ERROR;\n+    }\n+    header.value.len = value_len;\n+\n+    ngx_memcpy(header.value.data, value, value_len);\n+\n+    if (ngx_http_v3_validate_header(r, &header) != NGX_OK) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* Check for pseudo-header. */\n+    if (header.name.data[0] == ':') {\n+        rc = ngx_http_v3_pseudo_header(r, &header);\n+\n+        if (rc == NGX_OK) {\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                           \"http3 header: \\\":%V: %V\\\"\",\n+                           &header.name, &header.value);\n+\n+            return NGX_OK;\n+        }\n+\n+        return NGX_ERROR;\n+    }\n+\n+    if (r->invalid_header) {\n+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+        if (cscf->ignore_invalid_headers) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid header: \\\"%V\\\"\", &header.name);\n+\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    /* Handle Cookie header separately. Not sure why, but the HTTP/2 code does\n+     * the same. */\n+    if (header.name.len == cookie.len\n+        && ngx_memcmp(header.name.data, cookie.data, cookie.len) == 0)\n+    {\n+        if (ngx_http_v3_cookie(r, &header) != NGX_OK) {\n+            return NGX_ERROR;\n+        }\n+\n+    } else {\n+        h = ngx_list_push(&r->headers_in.headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->key.len = header.name.len;\n+        h->key.data = header.name.data;\n+\n+        /*\n+         * TODO Optimization: precalculate hash\n+         * and handler for indexed headers.\n+         */\n+        h->hash = ngx_hash_key(h->key.data, h->key.len);\n+\n+        h->value.len = header.value.len;\n+        h->value.data = header.value.data;\n+\n+        h->lowcase_key = h->key.data;\n+\n+        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+        hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n+                           h->lowcase_key, h->key.len);\n+\n+        if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 header: \\\"%V: %V\\\"\",\n+                   &header.name, &header.value);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_process_headers(ngx_connection_t *c, quiche_h3_event *ev,\n+    int64_t stream_id)\n+{\n+    int                        rc;\n+    ngx_http_v3_stream_t      *stream;\n+    ngx_http_v3_srv_conf_t    *h3scf;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 process headers\");\n+\n+    h3c = c->data;\n+\n+    h3scf = ngx_http_get_module_srv_conf(h3c->http_connection->conf_ctx,\n+                                         ngx_http_v3_module);\n+\n+    if (h3c->connection->requests >= h3scf->max_requests) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_NO_ERROR);\n+        return;\n+    }\n+\n+    /* Create a new stream to handle the incoming request. */\n+    stream = ngx_http_v3_create_stream(h3c);\n+    if (stream == NULL) {\n+        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to create HTTP/3 stream\");\n+\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    stream->id = stream_id;\n+\n+    stream->node.key = stream_id;\n+\n+    ngx_rbtree_insert(&h3c->streams, &stream->node);\n+\n+    /* Populate ngx_http_request_t from raw HTTP/3 headers. */\n+    rc = quiche_h3_event_for_each_header(ev,\n+        ngx_http_v3_for_each_header, stream->request);\n+\n+    if (rc != NGX_OK) {\n+        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n+                      \"received invalid HTTP/3 headers\");\n+\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    stream->in_closed = !quiche_h3_event_headers_has_body(ev);\n+\n+    ngx_http_v3_run_request(stream->request);\n+}\n+\n+\n+static void\n+ngx_http_v3_process_blocked_streams(ngx_http_v3_connection_t *h3c)\n+{\n+    ngx_event_t               *wev;\n+    ngx_http_v3_stream_t      *stream;\n+    ngx_quic_connection_t     *quic = h3c->connection->quic;\n+    int64_t                    stream_id;\n+\n+    while ((stream_id = quiche_conn_stream_writable_next(quic->conn)) >= 0) {\n+        stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+        if (stream == NULL) {\n+            continue;\n+        }\n+\n+        if (!stream->blocked) {\n+            continue;\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                       \"http3 stream unblocked %ui\", stream->id);\n+\n+        stream->blocked = 0;\n+\n+        wev = stream->request->connection->write;\n+\n+        wev->active = 0;\n+        wev->ready = 1;\n+\n+        if (!stream->headers_sent) {\n+            ngx_http_v3_send_response(stream->request);\n+        }\n+\n+        if (!wev->delayed) {\n+            wev->handler(wev);\n+        }\n+    }\n+}\n+\n+\n+static void\n+ngx_http_v3_handler(ngx_connection_t *c)\n+{\n+    ngx_chain_t                out;\n+    ngx_connection_t          *fc;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_connection_t  *h3c;\n+    ngx_http_v3_stream_t      *stream;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 handler\");\n+\n+    h3c = c->data;\n+\n+    if (c->read->timedout) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_PROTOCOL_ERROR);\n+        return;\n+    }\n+\n+    if (c->error) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    ngx_http_v3_process_blocked_streams(h3c);\n+\n+    while (!c->error) {\n+        quiche_h3_event  *ev;\n+\n+        int64_t stream_id = quiche_h3_conn_poll(h3c->h3, c->quic->conn, &ev);\n+        if (stream_id == QUICHE_H3_ERR_DONE) {\n+            break;\n+        }\n+\n+        if (stream_id < 0) {\n+            ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_PROTOCOL_ERROR);\n+            return;\n+        }\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                       \"http3 event stream:%ui ev:%ui\", stream_id,\n+                       quiche_h3_event_type(ev));\n+\n+        switch (quiche_h3_event_type(ev)) {\n+            case QUICHE_H3_EVENT_HEADERS: {\n+                ngx_http_v3_process_headers(c, ev, stream_id);\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_DATA: {\n+                /* Lookup stream. If there isn't one, it means it has already\n+                 * been closed, so ignore the event. */\n+                stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+                if (stream != NULL && !stream->in_closed) {\n+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                                   \"http3 data\");\n+\n+                    ngx_post_event(stream->request->connection->read,\n+                                   &ngx_posted_events);\n+                }\n+\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_FINISHED: {\n+                /* Lookup stream. If there isn't one, it means it has already\n+                 * been closed, so ignore the event. */\n+                stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+                if (stream != NULL && !stream->in_closed) {\n+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                                   \"http3 finished\");\n+\n+                    /* Flush request body that was buffered. */\n+                    if (stream->request->request_body) {\n+                        out.buf = stream->request->request_body->buf;\n+                        out.next = NULL;\n+\n+                        ngx_http_v3_request_body_filter(stream->request, &out);\n+\n+                        ngx_post_event(stream->request->connection->read,\n+                                       &ngx_posted_events);\n+                    }\n+\n+                    stream->in_closed = 1;\n+                }\n+\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_RESET: {\n+                /* Lookup stream. If there isn't one, it means it has already\n+                 * been closed, so ignore the event. */\n+                stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+                if (stream != NULL && !stream->in_closed) {\n+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                                   \"http3 reset\");\n+\n+                    r = stream->request;\n+                    fc = r->connection;\n+\n+                    fc->error = 1;\n+\n+                    ngx_post_event(stream->request->connection->read,\n+                                   &ngx_posted_events);\n+\n+                    stream->in_closed = 1;\n+                }\n+\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_PRIORITY_UPDATE:\n+                break;\n+\n+            case QUICHE_H3_EVENT_DATAGRAM:\n+                break;\n+\n+            case QUICHE_H3_EVENT_GOAWAY:\n+                break;\n+        }\n+\n+        quiche_h3_event_free(ev);\n+    }\n+\n+    ngx_http_v3_handle_connection(h3c);\n+}\n+\n+\n+static void\n+ngx_http_v3_idle_handler(ngx_connection_t *c)\n+{\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 idle handler\");\n+\n+    h3c = c->data;\n+\n+    if (c->read->timedout) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_NO_ERROR);\n+        return;\n+    }\n+\n+    if (c->error) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    if (!quiche_conn_is_readable(c->quic->conn)) {\n+        return;\n+    }\n+\n+    if (c->read->timer_set) {\n+        ngx_del_timer(c->read);\n+    }\n+\n+    c->quic->handler = ngx_http_v3_handler;\n+\n+    ngx_http_v3_handler(c);\n+}\n+\n+\n+static void\n+ngx_http_v3_handle_connection(ngx_http_v3_connection_t *h3c)\n+{\n+    ngx_connection_t        *c;\n+    ngx_http_v3_srv_conf_t  *h3scf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 handle connection\");\n+\n+    c = h3c->connection;\n+\n+    if (h3c->processing || c->error) {\n+        return;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 connection is idle\");\n+\n+    h3scf = ngx_http_get_module_srv_conf(h3c->http_connection->conf_ctx,\n+                                         ngx_http_v3_module);\n+\n+    c->quic->handler = ngx_http_v3_idle_handler;\n+\n+    ngx_add_timer(c->read, h3scf->idle_timeout);\n+}\n+\n+\n+static ngx_http_v3_stream_t *\n+ngx_http_v3_create_stream(ngx_http_v3_connection_t *h3c)\n+{\n+    ngx_log_t                 *log;\n+    ngx_event_t               *rev, *wev;\n+    ngx_connection_t          *fc;\n+    ngx_http_log_ctx_t        *ctx;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_stream_t      *stream;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 create stream\");\n+\n+    fc = h3c->free_fake_connections;\n+\n+    if (fc) {\n+        h3c->free_fake_connections = fc->data;\n+\n+        rev = fc->read;\n+        wev = fc->write;\n+        log = fc->log;\n+        ctx = log->data;\n+\n+    } else {\n+        fc = ngx_palloc(h3c->pool, sizeof(ngx_connection_t));\n+        if (fc == NULL) {\n+            return NULL;\n+        }\n+\n+        rev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));\n+        if (rev == NULL) {\n+            return NULL;\n+        }\n+\n+        wev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));\n+        if (wev == NULL) {\n+            return NULL;\n+        }\n+\n+        log = ngx_palloc(h3c->pool, sizeof(ngx_log_t));\n+        if (log == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx = ngx_palloc(h3c->pool, sizeof(ngx_http_log_ctx_t));\n+        if (ctx == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx->connection = fc;\n+        ctx->request = NULL;\n+        ctx->current_request = NULL;\n+    }\n+\n+    ngx_memcpy(log, h3c->connection->log, sizeof(ngx_log_t));\n+\n+    log->data = ctx;\n+\n+    ngx_memzero(rev, sizeof(ngx_event_t));\n+\n+    rev->data = fc;\n+    rev->ready = 1;\n+    rev->handler = ngx_http_v3_close_stream_handler;\n+    rev->log = log;\n+\n+    ngx_memcpy(wev, rev, sizeof(ngx_event_t));\n+\n+    wev->write = 1;\n+\n+    ngx_memcpy(fc, h3c->connection, sizeof(ngx_connection_t));\n+\n+    fc->data = h3c->http_connection;\n+    fc->quic = h3c->connection->quic;\n+    fc->read = rev;\n+    fc->write = wev;\n+    fc->sent = 0;\n+    fc->buffer = NULL;\n+    fc->log = log;\n+    fc->buffered = 0;\n+    fc->sndlowat = 1;\n+    fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n+\n+    fc->recv = ngx_http_v3_recv_body;\n+\n+    fc->send_chain = ngx_http_v3_send_chain;\n+    fc->need_last_buf = 1;\n+\n+    r = ngx_http_create_request(fc);\n+    if (r == NULL) {\n+        return NULL;\n+    }\n+\n+    ngx_str_set(&r->http_protocol, \"HTTP/3\");\n+\n+    r->http_version = NGX_HTTP_VERSION_3;\n+    r->valid_location = 1;\n+\n+    fc->data = r;\n+    h3c->connection->requests++;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    r->header_in = ngx_create_temp_buf(r->pool,\n+                                       cscf->client_header_buffer_size);\n+    if (r->header_in == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    if (ngx_list_init(&r->headers_in.headers, r->pool, 20,\n+                      sizeof(ngx_table_elt_t))\n+        != NGX_OK)\n+    {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n+\n+    stream = ngx_pcalloc(h3c->pool, sizeof(ngx_http_v3_stream_t));\n+    if (stream == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->qstream = stream;\n+\n+    stream->request = r;\n+    stream->connection = h3c;\n+\n+    h3c->processing++;\n+\n+    return stream;\n+}\n+\n+\n+static ngx_http_v3_stream_t *\n+ngx_http_v3_stream_lookup(ngx_http_v3_connection_t *h3c, ngx_uint_t stream_id)\n+{\n+    ngx_rbtree_node_t  *node, *sentinel;\n+\n+    node = h3c->streams.root;\n+    sentinel = h3c->streams.sentinel;\n+\n+    while (node != sentinel) {\n+\n+        if (stream_id < node->key) {\n+            node = node->left;\n+            continue;\n+        }\n+\n+        if (stream_id > node->key) {\n+            node = node->right;\n+            continue;\n+        }\n+\n+        /* stream_id == node->key */\n+\n+        return (ngx_http_v3_stream_t *) node;\n+    }\n+\n+    /* not found */\n+\n+    return NULL;\n+}\n+\n+\n+/* The following functions are copied from the HTTP/2 module, and adapted to\n+ * work independently. In theory we could refactor the HTTP/2 module to expose\n+ * these functions, but that would be fairly invasive and likely cause more\n+ * merge conflicts in the future. */\n+\n+\n+static ngx_int_t\n+ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n+{\n+    u_char                     ch;\n+    ngx_uint_t                 i;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    if (header->name.len == 0) {\n+        return NGX_ERROR;\n+    }\n+\n+    r->invalid_header = 0;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    for (i = (header->name.data[0] == ':'); i != header->name.len; i++) {\n+        ch = header->name.data[i];\n+\n+        if ((ch >= 'a' && ch <= 'z')\n+            || (ch == '-')\n+            || (ch >= '0' && ch <= '9')\n+            || (ch == '_' && cscf->underscores_in_headers))\n+        {\n+            continue;\n+        }\n+\n+        if (ch == '\\0' || ch == LF || ch == CR || ch == ':'\n+            || (ch >= 'A' && ch <= 'Z'))\n+        {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid header name: \\\"%V\\\"\",\n+                          &header->name);\n+\n+            return NGX_ERROR;\n+        }\n+\n+        r->invalid_header = 1;\n+    }\n+\n+    for (i = 0; i != header->value.len; i++) {\n+        ch = header->value.data[i];\n+\n+        if (ch == '\\0' || ch == LF || ch == CR) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent header \\\"%V\\\" with \"\n+                          \"invalid value: \\\"%V\\\"\",\n+                          &header->name, &header->value);\n+\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_pseudo_header(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n+{\n+    header->name.len--;\n+    header->name.data++;\n+\n+    switch (header->name.len) {\n+    case 4:\n+        if (ngx_memcmp(header->name.data, \"path\", sizeof(\"path\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_path(r, &header->value);\n+        }\n+\n+        break;\n+\n+    case 6:\n+        if (ngx_memcmp(header->name.data, \"method\", sizeof(\"method\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_method(r, &header->value);\n+        }\n+\n+        if (ngx_memcmp(header->name.data, \"scheme\", sizeof(\"scheme\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_scheme(r, &header->value);\n+        }\n+\n+        break;\n+\n+    case 9:\n+        if (ngx_memcmp(header->name.data, \"authority\", sizeof(\"authority\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_authority(r, &header->value);\n+        }\n+\n+        break;\n+    }\n+\n+    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                  \"client sent unknown pseudo-header \\\":%V\\\"\",\n+                  &header->name);\n+\n+    return NGX_DECLINED;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_path(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    if (r->unparsed_uri.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :path header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (value->len == 0) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent empty :path header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    r->uri_start = value->data;\n+    r->uri_end = value->data + value->len;\n+\n+    if (ngx_http_parse_uri(r) != NGX_OK) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent invalid :path header: \\\"%V\\\"\", value);\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (ngx_http_process_request_uri(r) != NGX_OK) {\n+        /*\n+         * request has been finalized already\n+         * in ngx_http_process_request_uri()\n+         */\n+        return NGX_ABORT;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_method(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    size_t         k, len;\n+    ngx_uint_t     n;\n+    const u_char  *p, *m;\n+\n+    /*\n+     * This array takes less than 256 sequential bytes,\n+     * and if typical CPU cache line size is 64 bytes,\n+     * it is prefetched for 4 load operations.\n+     */\n+    static const struct {\n+        u_char            len;\n+        const u_char      method[11];\n+        uint32_t          value;\n+    } tests[] = {\n+        { 3, \"GET\",       NGX_HTTP_GET },\n+        { 4, \"POST\",      NGX_HTTP_POST },\n+        { 4, \"HEAD\",      NGX_HTTP_HEAD },\n+        { 7, \"OPTIONS\",   NGX_HTTP_OPTIONS },\n+        { 8, \"PROPFIND\",  NGX_HTTP_PROPFIND },\n+        { 3, \"PUT\",       NGX_HTTP_PUT },\n+        { 5, \"MKCOL\",     NGX_HTTP_MKCOL },\n+        { 6, \"DELETE\",    NGX_HTTP_DELETE },\n+        { 4, \"COPY\",      NGX_HTTP_COPY },\n+        { 4, \"MOVE\",      NGX_HTTP_MOVE },\n+        { 9, \"PROPPATCH\", NGX_HTTP_PROPPATCH },\n+        { 4, \"LOCK\",      NGX_HTTP_LOCK },\n+        { 6, \"UNLOCK\",    NGX_HTTP_UNLOCK },\n+        { 5, \"PATCH\",     NGX_HTTP_PATCH },\n+        { 5, \"TRACE\",     NGX_HTTP_TRACE }\n+    }, *test;\n+\n+    if (r->method_name.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :method header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (value->len == 0) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent empty :method header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    r->method_name.len = value->len;\n+    r->method_name.data = value->data;\n+\n+    len = r->method_name.len;\n+    n = sizeof(tests) / sizeof(tests[0]);\n+    test = tests;\n+\n+    do {\n+        if (len == test->len) {\n+            p = r->method_name.data;\n+            m = test->method;\n+            k = len;\n+\n+            do {\n+                if (*p++ != *m++) {\n+                    goto next;\n+                }\n+            } while (--k);\n+\n+            r->method = test->value;\n+            return NGX_OK;\n+        }\n+\n+    next:\n+        test++;\n+\n+    } while (--n);\n+\n+    p = r->method_name.data;\n+\n+    do {\n+        if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid method: \\\"%V\\\"\",\n+                          &r->method_name);\n+\n+            return NGX_DECLINED;\n+        }\n+\n+        p++;\n+\n+    } while (--len);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    u_char      c, ch;\n+    ngx_uint_t  i;\n+\n+    if (r->schema.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :scheme header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (value->len == 0) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent empty :scheme header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    for (i = 0; i < value->len; i++) {\n+        ch = value->data[i];\n+\n+        c = (u_char) (ch | 0x20);\n+        if (c >= 'a' && c <= 'z') {\n+            continue;\n+        }\n+\n+        if (((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')\n+            && i > 0)\n+        {\n+            continue;\n+        }\n+\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent invalid :scheme header: \\\"%V\\\"\", value);\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    r->schema = *value;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_authority(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    return ngx_http_v3_parse_header(r, &ngx_http_v3_parse_headers[0], value);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_header(ngx_http_request_t *r,\n+    ngx_http_v3_parse_header_t *header, ngx_str_t *value)\n+{\n+    ngx_table_elt_t            *h;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    h->key.len = header->name.len;\n+    h->key.data = header->name.data;\n+    h->lowcase_key = header->name.data;\n+\n+    if (header->hh == NULL) {\n+        header->hash = ngx_hash_key(header->name.data, header->name.len);\n+\n+        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+        header->hh = ngx_hash_find(&cmcf->headers_in_hash, header->hash,\n+                                   h->lowcase_key, h->key.len);\n+        if (header->hh == NULL) {\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    h->hash = header->hash;\n+\n+    h->value.len = value->len;\n+    h->value.data = value->data;\n+\n+    if (header->hh->handler(r, h, header->hh->offset) != NGX_OK) {\n+        /* header handler has already finalized request */\n+        return NGX_ABORT;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_construct_request_line(ngx_http_request_t *r)\n+{\n+    u_char  *p;\n+\n+    static const u_char ending[] = \" HTTP/3\";\n+\n+    if (r->method_name.len == 0\n+        || r->schema.len == 0\n+        || r->unparsed_uri.len == 0)\n+    {\n+        if (r->method_name.len == 0) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent no :method header\");\n+\n+        } else if (r->schema.len == 0) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent no :scheme header\");\n+\n+        } else {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent no :path header\");\n+        }\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.len = r->method_name.len + 1\n+                          + r->unparsed_uri.len\n+                          + sizeof(ending) - 1;\n+\n+    p = ngx_pnalloc(r->pool, r->request_line.len + 1);\n+    if (p == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.data = p;\n+\n+    p = ngx_cpymem(p, r->method_name.data, r->method_name.len);\n+\n+    *p++ = ' ';\n+\n+    p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);\n+\n+    ngx_memcpy(p, ending, sizeof(ending));\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 request line: \\\"%V\\\"\", &r->request_line);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_cookie(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n+{\n+    ngx_str_t    *val;\n+    ngx_array_t  *cookies;\n+\n+    cookies = r->qstream->cookies;\n+\n+    if (cookies == NULL) {\n+        cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t));\n+        if (cookies == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        r->qstream->cookies = cookies;\n+    }\n+\n+    val = ngx_array_push(cookies);\n+    if (val == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    val->len = header->value.len;\n+    val->data = header->value.data;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_construct_cookie_header(ngx_http_request_t *r)\n+{\n+    u_char                     *buf, *p, *end;\n+    size_t                      len;\n+    ngx_str_t                  *vals;\n+    ngx_uint_t                  i;\n+    ngx_array_t                *cookies;\n+    ngx_table_elt_t            *h;\n+    ngx_http_header_t          *hh;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    static ngx_str_t cookie = ngx_string(\"cookie\");\n+\n+    cookies = r->qstream->cookies;\n+\n+    if (cookies == NULL) {\n+        return NGX_OK;\n+    }\n+\n+    vals = cookies->elts;\n+\n+    i = 0;\n+    len = 0;\n+\n+    do {\n+        len += vals[i].len + 2;\n+    } while (++i != cookies->nelts);\n+\n+    len -= 2;\n+\n+    buf = ngx_pnalloc(r->pool, len + 1);\n+    if (buf == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    p = buf;\n+    end = buf + len;\n+\n+    for (i = 0; /* void */ ; i++) {\n+\n+        p = ngx_cpymem(p, vals[i].data, vals[i].len);\n+\n+        if (p == end) {\n+            *p = '\\0';\n+            break;\n+        }\n+\n+        *p++ = ';'; *p++ = ' ';\n+    }\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(\n+                                    ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e');\n+\n+    h->key.len = cookie.len;\n+    h->key.data = cookie.data;\n+\n+    h->value.len = len;\n+    h->value.data = buf;\n+\n+    h->lowcase_key = cookie.data;\n+\n+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+    hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n+                       h->lowcase_key, h->key.len);\n+\n+    if (hh == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    if (hh->handler(r, h, hh->offset) != NGX_OK) {\n+        /*\n+         * request has been finalized already\n+         * in ngx_http_process_multi_header_lines()\n+         */\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_run_request(ngx_http_request_t *r)\n+{\n+    if (ngx_http_v3_construct_request_line(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    if (ngx_http_v3_construct_cookie_header(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;\n+\n+    if (ngx_http_process_request_header(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    if (r->headers_in.content_length_n > 0 && r->qstream->in_closed) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client prematurely closed stream\");\n+\n+        r->qstream->skip_data = 1;\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return;\n+    }\n+\n+    if (r->headers_in.content_length_n == -1 && !r->qstream->in_closed) {\n+        r->headers_in.chunked = 1;\n+    }\n+\n+    ngx_http_process_request(r);\n+}\n+\n+\n+/* End of functions copied from HTTP/2 module. */\n+\n+\n+ngx_int_t\n+ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n+{\n+    size_t                     size;\n+    ngx_int_t                  rc;\n+    ngx_buf_t                 *b;\n+    ngx_chain_t               *cl, *tl, *out, **ll;\n+    ngx_connection_t          *c;\n+    ngx_http_request_body_t   *rb;\n+    ngx_http_core_loc_conf_t  *clcf;\n+\n+    c = r->qstream->connection->connection;\n+\n+    rb = r->request_body;\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    if (rb->rest == -1) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"http3 request body filter\");\n+\n+        if (r->headers_in.chunked) {\n+            rb->rest = clcf->client_body_buffer_size;\n+            r->headers_in.content_length_n = 0;\n+        } else {\n+            rb->rest = r->headers_in.content_length_n;\n+        }\n+    }\n+\n+    out = NULL;\n+    ll = &out;\n+\n+    for (cl = in; cl; cl = cl->next) {\n+\n+        if (rb->rest == 0) {\n+            break;\n+        }\n+\n+        if (ngx_buf_size(cl->buf) == 0) {\n+            continue;\n+        }\n+\n+        tl = ngx_chain_get_free_buf(r->pool, &rb->free);\n+        if (tl == NULL) {\n+            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n+        }\n+\n+        b = tl->buf;\n+\n+        ngx_memzero(b, sizeof(ngx_buf_t));\n+\n+        b->temporary = 1;\n+        b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;\n+        b->start = cl->buf->pos;\n+        b->pos = cl->buf->pos;\n+        b->last = cl->buf->last;\n+        b->end = cl->buf->end;\n+        b->flush = r->request_body_no_buffering;\n+\n+        size = cl->buf->last - cl->buf->pos;\n+\n+        cl->buf->pos = cl->buf->last;\n+\n+        if (r->headers_in.chunked) {\n+            r->headers_in.content_length_n += size;\n+        }\n+\n+        if (quiche_conn_stream_finished(c->quic->conn, r->qstream->id)) {\n+            rb->rest = 0;\n+            b->last = cl->buf->pos;\n+            b->last_buf = 1;\n+        }\n+\n+        *ll = tl;\n+        ll = &tl->next;\n+    }\n+\n+    rc = ngx_http_top_request_body_filter(r, out);\n+\n+    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,\n+                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);\n+\n+    return rc;\n+}\n+\n+\n+size_t\n+ngx_http_v3_get_headers_out_count(ngx_http_request_t *r)\n+{\n+    size_t                     headers_count;\n+    ngx_uint_t                 i;\n+    ngx_list_part_t           *part;\n+    ngx_table_elt_t           *header;\n+\n+    headers_count = 1; /* :status */\n+\n+    if (r->headers_out.server == NULL) {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.date == NULL) {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.content_type.len) {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.content_length == NULL\n+        && r->headers_out.content_length_n >= 0)\n+    {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.last_modified == NULL\n+        && r->headers_out.last_modified_time != -1)\n+    {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.location && r->headers_out.location->value.len) {\n+        headers_count += 1;\n+    }\n+\n+#if (NGX_HTTP_GZIP)\n+    if (r->gzip_vary) {\n+        headers_count += 1;\n+    }\n+#endif\n+\n+    part = &r->headers_out.headers.part;\n+    header = part->elts;\n+\n+    for (i = 0; /* void */; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            header = part->elts;\n+            i = 0;\n+        }\n+\n+        if (header[i].hash == 0) {\n+            continue;\n+        }\n+\n+        headers_count += 1;\n+    }\n+\n+    return headers_count;\n+}\n+\n+\n+ngx_int_t\n+ngx_http_v3_push_response_headers(ngx_http_request_t *r)\n+{\n+    u_char                    *tmp;\n+    size_t                     len, headers_count;\n+    ngx_str_t                  host, location;\n+    ngx_uint_t                 i, port;\n+    ngx_list_part_t           *part;\n+    ngx_table_elt_t           *header;\n+    ngx_connection_t          *fc;\n+    quiche_h3_header          *h;\n+    ngx_http_core_loc_conf_t  *clcf;\n+    ngx_http_core_srv_conf_t  *cscf;\n+    u_char                     addr[NGX_SOCKADDR_STRLEN];\n+\n+    /* The list of response headers was already generated, so there's nothing\n+     * more to do here. */\n+    if (r->qstream->headers != NULL) {\n+        return NGX_OK;\n+    }\n+\n+    fc = r->connection;\n+\n+    if (r->method == NGX_HTTP_HEAD) {\n+        r->header_only = 1;\n+    }\n+\n+    switch (r->headers_out.status) {\n+\n+    case NGX_HTTP_OK:\n+        break;\n+\n+    case NGX_HTTP_NO_CONTENT:\n+        r->header_only = 1;\n+\n+        ngx_str_null(&r->headers_out.content_type);\n+\n+        r->headers_out.content_length = NULL;\n+        r->headers_out.content_length_n = -1;\n+\n+        r->headers_out.last_modified_time = -1;\n+        r->headers_out.last_modified = NULL;\n+        break;\n+\n+    case NGX_HTTP_PARTIAL_CONTENT:\n+        break;\n+\n+    case NGX_HTTP_NOT_MODIFIED:\n+        r->header_only = 1;\n+        break;\n+\n+    default:\n+        r->headers_out.last_modified_time = -1;\n+        r->headers_out.last_modified = NULL;\n+    }\n+\n+    headers_count = ngx_http_v3_get_headers_out_count(r);\n+\n+    r->qstream->headers =\n+        ngx_array_create(r->pool, headers_count, sizeof(quiche_h3_header));\n+\n+    if (r->qstream->headers == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* Generate :status pseudo-header. */\n+    {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \":status\";\n+        h->name_len = sizeof(\":status\") - 1;\n+\n+        tmp = ngx_pnalloc(r->pool, sizeof(\"418\") - 1);\n+        if (tmp == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->value = tmp;\n+        h->value_len = ngx_sprintf(tmp, \"%03ui\", r->headers_out.status) - tmp;\n+    }\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    /* Generate Server header.*/\n+    if (r->headers_out.server == NULL) {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"server\";\n+        h->name_len = sizeof(\"server\") - 1;\n+\n+        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n+            h->value = (u_char *) NGINX_VER;\n+            h->value_len = sizeof(NGINX_VER) - 1;\n+\n+        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n+            h->value = (u_char *) NGINX_VER_BUILD;\n+            h->value_len = sizeof(NGINX_VER_BUILD) - 1;\n+\n+        } else {\n+            h->value = (u_char *) \"nginx\";\n+            h->value_len = sizeof(\"nginx\") - 1;\n+        }\n+    }\n+\n+    /* Generate Date header. */\n+    if (r->headers_out.date == NULL) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"date: %V\\\"\",\n+                       &ngx_cached_http_time);\n+\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"date\";\n+        h->name_len = sizeof(\"date\") - 1;\n+\n+        h->value = ngx_cached_http_time.data;\n+        h->value_len = ngx_cached_http_time.len;\n+    }\n+\n+    /* Generate Content-Type header. */\n+    if (r->headers_out.content_type.len) {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n+            && r->headers_out.charset.len)\n+        {\n+            len = r->headers_out.content_type.len + sizeof(\"; charset=\") - 1\n+                  + r->headers_out.charset.len;\n+\n+            tmp = ngx_pnalloc(r->pool, len);\n+            if (tmp == NULL) {\n+                return NGX_ERROR;\n+            }\n+\n+            tmp = ngx_cpymem(tmp, r->headers_out.content_type.data,\n+                             r->headers_out.content_type.len);\n+\n+            tmp = ngx_cpymem(tmp, \"; charset=\", sizeof(\"; charset=\") - 1);\n+\n+            tmp = ngx_cpymem(tmp, r->headers_out.charset.data,\n+                             r->headers_out.charset.len);\n+\n+            /* updated r->headers_out.content_type is also needed for logging */\n+\n+            r->headers_out.content_type.len = len;\n+            r->headers_out.content_type.data = tmp - len;\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"content-type: %V\\\"\",\n+                       &r->headers_out.content_type);\n+\n+        h->name = (u_char *) \"content-type\";\n+        h->name_len = sizeof(\"content-type\") - 1;\n+\n+        h->value = r->headers_out.content_type.data;\n+        h->value_len = r->headers_out.content_type.len;\n+    }\n+\n+    /* Generate Content-Length header. */\n+    if (r->headers_out.content_length == NULL\n+        && r->headers_out.content_length_n >= 0)\n+    {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"content-length\";\n+        h->name_len = sizeof(\"content-length\") - 1;\n+\n+        tmp = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n+        if (tmp == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->value = tmp;\n+        h->value_len =\n+            ngx_sprintf(tmp, \"%O\", r->headers_out.content_length_n) - tmp;\n+    }\n+\n+    /* Generate Last-Modified header. */\n+    if (r->headers_out.last_modified == NULL\n+        && r->headers_out.last_modified_time != -1)\n+    {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"last-modified\";\n+        h->name_len = sizeof(\"last-modified\") - 1;\n+\n+        tmp = ngx_pnalloc(r->pool, sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1);\n+        if (tmp == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->value = tmp;\n+        h->value_len =\n+            ngx_http_time(tmp, r->headers_out.last_modified_time) - tmp;\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"last-modified: %*.s\\\"\",\n+                       h->value_len, h->value);\n+    }\n+\n+    /* Generate Location header. */\n+    if (r->headers_out.location && r->headers_out.location->value.len) {\n+\n+        if (r->headers_out.location->value.data[0] == '/'\n+            && clcf->absolute_redirect)\n+        {\n+            if (clcf->server_name_in_redirect) {\n+                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+                host = cscf->server_name;\n+\n+            } else if (r->headers_in.server.len) {\n+                host = r->headers_in.server;\n+\n+            } else {\n+                host.data = addr;\n+                host.len = NGX_SOCKADDR_STRLEN;\n+\n+                if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {\n+                    return NGX_ERROR;\n+                }\n+            }\n+\n+            port = ngx_inet_get_port(fc->local_sockaddr);\n+\n+            location.len = sizeof(\"https://\") - 1 + host.len\n+                           + r->headers_out.location->value.len;\n+\n+            if (clcf->port_in_redirect) {\n+\n+#if (NGX_HTTP_SSL)\n+                if (fc->ssl)\n+                    port = (port == 443) ? 0 : port;\n+                else\n+#endif\n+                    port = (port == 80) ? 0 : port;\n+\n+            } else {\n+                port = 0;\n+            }\n+\n+            if (port) {\n+                location.len += sizeof(\":65535\") - 1;\n+            }\n+\n+            location.data = ngx_pnalloc(r->pool, location.len);\n+            if (location.data == NULL) {\n+                return NGX_ERROR;\n+            }\n+\n+            tmp = ngx_cpymem(location.data, \"http\", sizeof(\"http\") - 1);\n+\n+#if (NGX_HTTP_SSL)\n+            if (fc->ssl) {\n+                *tmp++ = 's';\n+            }\n+#endif\n+\n+            *tmp++ = ':'; *tmp++ = '/'; *tmp++ = '/';\n+            tmp = ngx_cpymem(tmp, host.data, host.len);\n+\n+            if (port) {\n+                tmp = ngx_sprintf(tmp, \":%ui\", port);\n+            }\n+\n+            tmp = ngx_cpymem(tmp, r->headers_out.location->value.data,\n+                                  r->headers_out.location->value.len);\n+\n+            /* update r->headers_out.location->value for possible logging */\n+\n+            r->headers_out.location->value.len = tmp - location.data;\n+            r->headers_out.location->value.data = location.data;\n+            ngx_str_set(&r->headers_out.location->key, \"Location\");\n+        }\n+\n+        r->headers_out.location->hash = 0;\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"location: %V\\\"\",\n+                       &r->headers_out.location->value);\n+\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"location\";\n+        h->name_len = sizeof(\"location\") - 1;\n+\n+        h->value = r->headers_out.location->value.data;\n+        h->value_len = r->headers_out.location->value.len;\n+    }\n+\n+#if (NGX_HTTP_GZIP)\n+    /* Generate Vary header. */\n+    if (r->gzip_vary) {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"vary: Accept-Encoding\\\"\");\n+\n+        h->name = (u_char *) \"vary\";\n+        h->name_len = sizeof(\"vary\") - 1;\n+\n+        h->value = (u_char *) \"Accept-Encoding\";\n+        h->value_len = sizeof(\"Accept-Encoding\") - 1;\n+    }\n+#endif\n+\n+    part = &r->headers_out.headers.part;\n+    header = part->elts;\n+\n+    /* Generate all other headers. */\n+    for (i = 0; /* void */; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            header = part->elts;\n+            i = 0;\n+        }\n+\n+        if (header[i].hash == 0) {\n+            continue;\n+        }\n+\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+#if (NGX_DEBUG)\n+        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                           \"http3 output header: \\\"%V: %V\\\"\",\n+                           &header[i].key, &header[i].value);\n+        }\n+#endif\n+\n+        h->name = header[i].key.data;\n+        h->name_len = header[i].key.len;\n+\n+        h->value = header[i].value.data;\n+        h->value_len = header[i].value.len;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_http_v3_send_response(ngx_http_request_t *r)\n+{\n+    int                        rc;\n+    ngx_uint_t                 fin;\n+    ngx_connection_t          *c, *fc;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 send response stream %ui\", r->qstream->id);\n+\n+    fc = r->connection;\n+\n+    if (fc->error) {\n+        return NGX_ERROR;\n+    }\n+\n+    h3c = r->qstream->connection;\n+    c = h3c->connection;\n+\n+    if (ngx_http_v3_push_response_headers(r) != NGX_OK) {\n+        return NGX_ERROR;\n+    }\n+\n+    fin = r->header_only\n+          || (r->headers_out.content_length_n == 0 && !r->expect_trailers);\n+\n+    rc = quiche_h3_send_response(h3c->h3, c->quic->conn, r->qstream->id,\n+                                 r->qstream->headers->elts,\n+                                 r->qstream->headers->nelts,\n+                                 fin);\n+\n+    if (rc == QUICHE_H3_ERR_STREAM_BLOCKED) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"http3 stream blocked %ui\", r->qstream->id);\n+\n+        r->qstream->blocked = 1;\n+\n+        fc->write->active = 1;\n+        fc->write->ready = 0;\n+\n+        return NGX_AGAIN;\n+    }\n+\n+    if (rc != NGX_OK) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (fin) {\n+        r->qstream->out_closed = 1;\n+    }\n+\n+    r->qstream->headers_sent = 1;\n+\n+    if (r->done) {\n+        fc->write->handler = ngx_http_v3_close_stream_handler;\n+        fc->read->handler = ngx_http_empty_handler;\n+    }\n+\n+    ngx_post_event(c->write, &ngx_posted_events);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ssize_t\n+ngx_http_v3_stream_do_send(ngx_connection_t *fc, ngx_buf_t *b, ngx_int_t fin)\n+{\n+    ssize_t                    n;\n+    ngx_connection_t          *c;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_connection_t  *h3c;\n+    ngx_http_v3_stream_t      *stream;\n+\n+    uint8_t *buf = b ? b->pos : NULL;\n+    size_t buf_len = b ? ngx_buf_size(b) : 0;\n+\n+    r = fc->data;\n+    stream = r->qstream;\n+    h3c = stream->connection;\n+    c = h3c->connection;\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, fc->log, 0,\n+                   \"http3 stream %uz to write %uz bytes, fin=%d\",\n+                   stream->id, buf_len, fin);\n+\n+    if (!stream->headers_sent) {\n+        return NGX_AGAIN;\n+    }\n+\n+    n = quiche_h3_send_body(h3c->h3, c->quic->conn, r->qstream->id,\n+                            buf, buf_len, fin);\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, stream->connection->connection->log, 0,\n+                   \"http3 stream written %z bytes\", n);\n+\n+    if (n == QUICHE_H3_ERR_DONE) {\n+        return NGX_AGAIN;\n+    }\n+\n+    if (n < 0) {\n+        ngx_log_error(NGX_LOG_ERR, fc->log, 0, \"stream write failed: %d\", n);\n+        return NGX_ERROR;\n+    }\n+\n+    return n;\n+}\n+\n+\n+static ssize_t\n+ngx_http_v3_recv_body(ngx_connection_t *c, u_char *buf, size_t size)\n+{\n+    ssize_t                    n;\n+    ngx_event_t               *rev;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    rev = c->read;\n+\n+    r = c->data;\n+    h3c = r->qstream->connection;\n+\n+    if (c->error) {\n+        rev->ready = 0;\n+\n+        return NGX_ERROR;\n+    }\n+\n+    n = quiche_h3_recv_body(h3c->h3, c->quic->conn, r->qstream->id, buf, size);\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                   \"http3 body recv: %z of %uz\", n, size);\n+\n+    if (quiche_conn_stream_finished(c->quic->conn, r->qstream->id)) {\n+        rev->ready = 0;\n+\n+        /* Re-schedule connection read event to poll for Finished event. */\n+        ngx_post_event(h3c->connection->read, &ngx_posted_events);\n+    }\n+\n+    if (n == 0) {\n+        rev->ready = 0;\n+\n+        return 0;\n+    }\n+\n+    if (n > 0) {\n+\n+        if ((size_t) n < size) {\n+            rev->ready = 0;\n+        }\n+\n+        return n;\n+    }\n+\n+    if (n == QUICHE_H3_ERR_DONE) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quiche_h3_recv_body() not ready\");\n+\n+        n = NGX_AGAIN;\n+\n+    } else {\n+        rev->error = 1;\n+\n+        n = NGX_ERROR;\n+    }\n+\n+    rev->ready = 0;\n+\n+    return n;\n+}\n+\n+\n+static ngx_chain_t *\n+ngx_http_v3_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)\n+{\n+    ssize_t                n, sent;\n+    off_t                  send, prev_send;\n+    ngx_uint_t             blocked, fin;\n+\n+    ngx_http_request_t    *r;\n+    ngx_http_v3_stream_t  *stream;\n+\n+    r = fc->data;\n+    stream = r->qstream;\n+\n+    send = 0;\n+\n+    blocked = 0;\n+\n+    while (in) {\n+        off_t size = ngx_buf_size(in->buf);\n+\n+        if (size || in->buf->last_buf) {\n+            break;\n+        }\n+\n+        in = in->next;\n+    }\n+\n+    if (in == NULL || stream->out_closed) {\n+        return NULL;\n+    }\n+\n+    while (in) {\n+        prev_send = send;\n+\n+        fin = in->buf->last_buf;\n+\n+        send += ngx_buf_size(in->buf);\n+\n+        n = ngx_http_v3_stream_do_send(fc, in->buf, fin);\n+\n+        if (n == NGX_ERROR) {\n+            return NGX_CHAIN_ERROR;\n+        }\n+\n+        sent = (n == NGX_AGAIN) ? 0 : n;\n+\n+        fc->sent += sent;\n+\n+        in->buf->pos += sent;\n+\n+        /* Partial (or no) write, end now. */\n+        if ((n == NGX_AGAIN) || (send - prev_send != sent)) {\n+            blocked = 1;\n+            break;\n+        }\n+\n+        /* Buffer is fully written, switch to the next. */\n+        if (in->buf->pos == in->buf->last) {\n+            in = in->next;\n+        }\n+\n+        if (fin) {\n+            stream->out_closed = 1;\n+        }\n+    }\n+\n+    if (blocked) {\n+        if (!stream->blocked) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, stream->connection->connection->log, 0,\n+                           \"http3 stream blocked %ui\", stream->id);\n+\n+            stream->blocked = 1;\n+\n+            fc->write->active = 1;\n+            fc->write->ready = 0;\n+        }\n+    }\n+\n+    ngx_post_event(stream->connection->connection->write, &ngx_posted_events);\n+\n+    return in;\n+}\n+\n+\n+void\n+ngx_http_v3_close_stream(ngx_http_v3_stream_t *stream, ngx_int_t rc)\n+{\n+    ngx_event_t               *ev;\n+    ngx_connection_t          *fc;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    h3c = stream->connection;\n+\n+    fc = stream->request->connection;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 close stream %ui\", stream->id);\n+\n+    if (stream->blocked) {\n+        fc->write->handler = ngx_http_v3_close_stream_handler;\n+        fc->read->handler = ngx_http_empty_handler;\n+        return;\n+    }\n+\n+    quiche_conn_stream_shutdown(h3c->connection->quic->conn, stream->id,\n+                                QUICHE_SHUTDOWN_READ, 0);\n+\n+    ngx_rbtree_delete(&h3c->streams, &stream->node);\n+\n+    ngx_http_free_request(stream->request, rc);\n+\n+    ev = fc->read;\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    ev = fc->write;\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    fc->data = h3c->free_fake_connections;\n+    h3c->free_fake_connections = fc;\n+\n+    h3c->processing--;\n+\n+    ngx_http_v3_handle_connection(h3c);\n+}\n+\n+\n+static void\n+ngx_http_v3_close_stream_handler(ngx_event_t *ev)\n+{\n+    ngx_connection_t    *fc;\n+    ngx_http_request_t  *r;\n+\n+    fc = ev->data;\n+    r = fc->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                   \"http3 close stream handler\");\n+\n+    if (ev->timedout) {\n+        ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, \"client timed out\");\n+\n+        fc->timedout = 1;\n+\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_REQUEST_TIME_OUT);\n+        return;\n+    }\n+\n+    ngx_http_v3_close_stream(r->qstream, 0);\n+}\n+\n+void\n+ngx_http_v3_stop_stream_read(ngx_http_v3_stream_t *stream, ngx_int_t rc)\n+{\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    if (!stream) {\n+        return;\n+    }\n+\n+    h3c = stream->connection;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 stream shutdown read %ui\", stream->id);\n+\n+    quiche_conn_stream_shutdown(h3c->connection->quic->conn,\n+                                stream->id,\n+                                QUICHE_SHUTDOWN_READ, rc);\n+}\n+\n+\n+static void\n+ngx_http_v3_finalize_connection(ngx_http_v3_connection_t *h3c,\n+    ngx_uint_t status)\n+{\n+    ngx_event_t             *ev;\n+    ngx_connection_t        *c, *fc;\n+    ngx_rbtree_node_t       *node, *root, *sentinel;\n+    ngx_http_request_t      *r;\n+    ngx_http_v3_stream_t    *stream;\n+\n+    c = h3c->connection;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 finalize connection\");\n+\n+    quiche_conn_close(c->quic->conn, true, status, NULL, 0);\n+\n+    c->error = 1;\n+\n+    if (!h3c->processing) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    c->read->handler = ngx_http_empty_handler;\n+    c->write->handler = ngx_http_empty_handler;\n+\n+    root = h3c->streams.root;\n+    sentinel = h3c->streams.sentinel;\n+\n+    if (root != sentinel) {\n+        node = ngx_rbtree_min(h3c->streams.root, sentinel);\n+    } else {\n+        node = NULL;\n+    }\n+\n+    /* Close all pending streams / requests. */\n+    while (node != NULL) {\n+        stream = (ngx_http_v3_stream_t *) node;\n+\n+        r = stream->request;\n+        fc = r->connection;\n+\n+        fc->error = 1;\n+\n+        if (c->close) {\n+            fc->close = 1;\n+        }\n+\n+        if (stream->blocked) {\n+            stream->blocked = 0;\n+\n+            ev = fc->write;\n+            ev->active = 0;\n+            ev->ready = 1;\n+\n+        } else {\n+            ev = fc->read;\n+        }\n+\n+        node = ngx_rbtree_next(&h3c->streams, node);\n+\n+        ev->eof = 1;\n+        ev->handler(ev);\n+    }\n+\n+    if (h3c->processing) {\n+        return;\n+    }\n+\n+    ngx_http_close_connection(c);\n+}\n+\n+\n+static void\n+ngx_http_v3_pool_cleanup(void *data)\n+{\n+    ngx_http_v3_connection_t  *h3c = data;\n+\n+    if (h3c->h3) {\n+        quiche_h3_conn_free(h3c->h3);\n+\n+        h3c->h3 = NULL;\n+    }\n+}\ndiff --color -uNr a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c\n--- a/src/http/v3/ngx_http_v3_filter_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3_filter_module.c\t2023-03-12 11:07:31.424412108 +0800\n@@ -0,0 +1,68 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+\n+static ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf);\n+\n+\n+static ngx_http_module_t  ngx_http_v3_filter_module_ctx = {\n+    NULL,                                  /* preconfiguration */\n+    ngx_http_v3_filter_init,               /* postconfiguration */\n+\n+    NULL,                                  /* create main configuration */\n+    NULL,                                  /* init main configuration */\n+\n+    NULL,                                  /* create server configuration */\n+    NULL,                                  /* merge server configuration */\n+\n+    NULL,                                  /* create location configuration */\n+    NULL                                   /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_v3_filter_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_v3_filter_module_ctx,        /* module context */\n+    NULL,                                  /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\n+\n+\n+static ngx_int_t\n+ngx_http_v3_header_filter(ngx_http_request_t *r)\n+{\n+    if (!r->qstream) {\n+        return ngx_http_next_header_filter(r);\n+    }\n+\n+    return ngx_http_v3_send_response(r);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_filter_init(ngx_conf_t *cf)\n+{\n+    ngx_http_next_header_filter = ngx_http_top_header_filter;\n+    ngx_http_top_header_filter = ngx_http_v3_header_filter;\n+\n+    return NGX_OK;\n+}\ndiff --color -uNr a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h\n--- a/src/http/v3/ngx_http_v3.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3.h\t2023-03-12 11:07:31.424412108 +0800\n@@ -0,0 +1,79 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#ifndef _NGX_HTTP_V3_H_INCLUDED_\n+#define _NGX_HTTP_V3_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+\n+#define NGX_HTTP_V3_ALPN_ADVERTISE       \"\\x05h3-18\"\n+\n+\n+typedef struct ngx_http_v3_connection_s   ngx_http_v3_connection_t;\n+\n+\n+struct ngx_http_v3_connection_s {\n+    quiche_h3_conn             *h3;\n+\n+    ngx_connection_t           *connection;\n+    ngx_http_connection_t      *http_connection;\n+\n+    ngx_pool_t                 *pool;\n+\n+    ngx_uint_t                  processing;\n+\n+    ngx_rbtree_t                streams;\n+    ngx_rbtree_node_t           streams_sentinel;\n+\n+    ngx_connection_t           *free_fake_connections;\n+};\n+\n+\n+struct ngx_http_v3_stream_s {\n+    ngx_rbtree_node_t          node;\n+\n+    uint64_t                   id;\n+\n+    ngx_http_request_t        *request;\n+\n+    ngx_http_v3_connection_t  *connection;\n+\n+    ngx_array_t               *headers;\n+    ngx_array_t               *cookies;\n+\n+    ngx_http_v3_stream_t      *next;\n+\n+    ngx_uint_t                 headers_sent:1;\n+    ngx_uint_t                 in_closed:1;\n+    ngx_uint_t                 out_closed:1;\n+    ngx_uint_t                 skip_data:1;\n+    ngx_uint_t                 blocked:1;\n+};\n+\n+\n+typedef struct {\n+    ngx_str_t                        name;\n+    ngx_str_t                        value;\n+} ngx_http_v3_header_t;\n+\n+\n+void ngx_http_v3_init(ngx_event_t *rev);\n+\n+ngx_int_t ngx_http_v3_send_response(ngx_http_request_t *r);\n+\n+void ngx_http_v3_close_stream(ngx_http_v3_stream_t *stream, ngx_int_t rc);\n+void ngx_http_v3_stop_stream_read(ngx_http_v3_stream_t *stream, ngx_int_t rc);\n+\n+ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r,\n+    ngx_chain_t *in);\n+\n+\n+#endif /* _NGX_HTTP_V3_H_INCLUDED_ */\ndiff --color -uNr a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c\n--- a/src/http/v3/ngx_http_v3_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3_module.c\t2023-03-12 11:07:31.424412108 +0800\n@@ -0,0 +1,286 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+#include <quiche.h>\n+\n+\n+static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf);\n+\n+static void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf);\n+static char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf,\n+    void *parent, void *child);\n+\n+static ngx_int_t ngx_http_v3_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data);\n+\n+static void ngx_http_v3_cleanup_ctx(void *data);\n+\n+\n+static ngx_command_t  ngx_http_v3_commands[] = {\n+\n+    { ngx_string(\"http3_max_concurrent_streams\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, concurrent_streams),\n+      NULL },\n+\n+    { ngx_string(\"http3_max_requests\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_requests),\n+      NULL },\n+\n+    { ngx_string(\"http3_max_header_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_header_size),\n+      NULL },\n+\n+    { ngx_string(\"http3_initial_max_data\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_data),\n+      NULL },\n+\n+    { ngx_string(\"http3_initial_max_stream_data\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_stream_data),\n+      NULL },\n+\n+    { ngx_string(\"http3_idle_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, idle_timeout),\n+      NULL },\n+\n+      ngx_null_command\n+};\n+\n+\n+static ngx_http_module_t  ngx_http_v3_module_ctx = {\n+    ngx_http_v3_add_variables,             /* preconfiguration */\n+    NULL,                                  /* postconfiguration */\n+\n+    NULL,                                  /* create main configuration */\n+    NULL,                                  /* init main configuration */\n+\n+    ngx_http_v3_create_srv_conf,           /* create server configuration */\n+    ngx_http_v3_merge_srv_conf,            /* merge server configuration */\n+\n+    NULL,                                  /* create location configuration */\n+    NULL                                   /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_v3_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_v3_module_ctx,             /* module context */\n+    ngx_http_v3_commands,                /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_variable_t ngx_http_v3_variables[] = {\n+\n+    { ngx_string(\"http3\"), NULL,\n+      ngx_http_v3_variable, 0,\n+      NGX_HTTP_VAR_CHANGEABLE, 0 },\n+\n+      ngx_http_null_variable\n+};\n+\n+\n+static ngx_int_t\n+ngx_http_v3_add_variables(ngx_conf_t *cf)\n+{\n+    ngx_http_variable_t *var, *v;\n+\n+    for (v = ngx_http_v3_variables; v->name.len; v++) {\n+        var = ngx_http_add_variable(cf, &v->name, v->flags);\n+        if (var == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        var->get_handler = v->get_handler;\n+        var->data = v->data;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void *\n+ngx_http_v3_create_srv_conf(ngx_conf_t *cf)\n+{\n+    ngx_http_v3_srv_conf_t  *h3scf;\n+\n+    h3scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t));\n+    if (h3scf == NULL) {\n+        return NULL;\n+    }\n+\n+    h3scf->idle_timeout = NGX_CONF_UNSET_MSEC;\n+    h3scf->max_data = NGX_CONF_UNSET_SIZE;\n+    h3scf->max_stream_data = NGX_CONF_UNSET_SIZE;\n+    h3scf->max_requests = NGX_CONF_UNSET_UINT;\n+    h3scf->max_header_size = NGX_CONF_UNSET_SIZE;\n+    h3scf->concurrent_streams = NGX_CONF_UNSET_UINT;\n+\n+    return h3scf;\n+}\n+\n+\n+#if (NGX_DEBUG)\n+static void\n+quiche_log(const char *line, void *argp)\n+{\n+    ngx_log_t *log = ngx_cycle->log;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, \"%s\", line);\n+}\n+#endif\n+\n+\n+static char *\n+ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n+{\n+    ngx_http_v3_srv_conf_t *prev = parent;\n+    ngx_http_v3_srv_conf_t *conf = child;\n+\n+    ngx_pool_cleanup_t  *cln;\n+\n+    ngx_conf_merge_msec_value(conf->idle_timeout,\n+                              prev->idle_timeout, 180000);\n+\n+    ngx_conf_merge_size_value(conf->max_data,\n+                              prev->max_data, 10485760);\n+\n+    ngx_conf_merge_size_value(conf->max_stream_data,\n+                              prev->max_stream_data, 1048576);\n+\n+    ngx_conf_merge_uint_value(conf->max_requests,\n+                              prev->max_requests, 1000);\n+\n+    ngx_conf_merge_size_value(conf->max_header_size,\n+                              prev->max_header_size, 16384);\n+\n+    ngx_conf_merge_uint_value(conf->concurrent_streams,\n+                              prev->concurrent_streams, 128);\n+\n+    conf->quic.log = cf->log;\n+\n+#if (NGX_DEBUG)\n+    /* Enable quiche debug logging. quiche commit ceade4 or later is required */\n+    quiche_enable_debug_logging(quiche_log, NULL);\n+#endif\n+\n+    if (ngx_quic_create_conf(&conf->quic) != NGX_OK) {\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    quiche_config_set_max_idle_timeout(conf->quic.config, conf->idle_timeout);\n+\n+    quiche_config_set_initial_max_data(conf->quic.config, conf->max_data);\n+\n+    quiche_config_set_initial_max_stream_data_bidi_remote(conf->quic.config,\n+                                                          conf->max_stream_data);\n+\n+    quiche_config_set_initial_max_stream_data_uni(conf->quic.config,\n+                                                  conf->max_stream_data);\n+\n+    quiche_config_set_initial_max_streams_bidi(conf->quic.config,\n+                                               conf->concurrent_streams);\n+\n+    /* For HTTP/3 we only need 3 unidirectional streams. */\n+    quiche_config_set_initial_max_streams_uni(conf->quic.config, 3);\n+\n+    conf->http3 = quiche_h3_config_new();\n+    if (conf->http3 == NULL) {\n+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                           \"failed to create HTTP/3 config\");\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    quiche_h3_config_set_max_field_section_size(conf->http3,\n+                                                conf->max_header_size);\n+\n+    cln = ngx_pool_cleanup_add(cf->pool, 0);\n+    if (cln == NULL) {\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    cln->handler = ngx_quic_cleanup_ctx;\n+    cln->data = &conf->quic;\n+\n+    cln = ngx_pool_cleanup_add(cf->pool, 0);\n+    if (cln == NULL) {\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    cln->handler = ngx_http_v3_cleanup_ctx;\n+    cln->data = conf->http3;\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n+    uintptr_t data)\n+{\n+    ngx_connection_t   *c;\n+\n+    v->valid = 1;\n+    v->no_cacheable = 1;\n+    v->not_found = 0;\n+\n+    c = r->connection;\n+    if (c == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (c->quic != NULL) {\n+        v->len = sizeof(\"h3\") - 1;\n+        v->valid = 1;\n+        v->no_cacheable = 0;\n+        v->not_found = 0;\n+        v->data = (u_char *) \"h3\";\n+\n+        return NGX_OK;\n+    }\n+\n+    *v = ngx_http_variable_null_value;\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_cleanup_ctx(void *data)\n+{\n+    quiche_h3_config  *config = data;\n+\n+    quiche_h3_config_free(config);\n+}\ndiff --color -uNr a/src/http/v3/ngx_http_v3_module.h b/src/http/v3/ngx_http_v3_module.h\n--- a/src/http/v3/ngx_http_v3_module.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3_module.h\t2023-03-12 11:07:31.424412108 +0800\n@@ -0,0 +1,34 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#ifndef _NGX_HTTP_V3_MODULE_H_INCLUDED_\n+#define _NGX_HTTP_V3_MODULE_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+\n+#include <quiche.h>\n+\n+\n+typedef struct {\n+    ngx_quic_t                      quic;\n+\n+    quiche_h3_config                *http3;\n+\n+    ngx_msec_t                      idle_timeout;\n+    size_t                          max_data;\n+    size_t                          max_stream_data;\n+    ngx_uint_t                      max_requests;\n+    ngx_uint_t                      max_header_size;\n+    ngx_uint_t                      concurrent_streams;\n+} ngx_http_v3_srv_conf_t;\n+\n+\n+extern ngx_module_t  ngx_http_v3_module;\n+\n+\n+#endif /* _NGX_HTTP_V3_MODULE_H_INCLUDED_ */\ndiff --color -uNr a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_chain.c\n--- a/src/os/unix/ngx_udp_sendmsg_chain.c\t2022-12-13 23:53:53.000000000 +0800\n+++ b/src/os/unix/ngx_udp_sendmsg_chain.c\t2023-03-12 11:07:31.424412108 +0800\n@@ -403,6 +403,7 @@\n \n         switch (err) {\n         case NGX_EAGAIN:\n+        case ENOBUFS:\n             ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                            \"sendmsg() not ready\");\n             return NGX_AGAIN;\n"
  },
  {
    "path": "nginx_with_quic_for_1.19.6.patch",
    "content": "diff --color -uNr a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c\n--- a/src/http/ngx_http_request.c\t2021-04-09 18:22:29.419033226 +0800\n+++ b/src/http/ngx_http_request.c\t2021-04-09 18:24:32.080095491 +0800\n@@ -361,8 +361,7 @@\n \n         /* We already have a UDP packet in the connection buffer, so we don't\n          * need to wait for another read event to kick-off the handshake. */\n-        cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);\n-        ngx_add_timer(rev, cscf->client_header_timeout);\n+        ngx_add_timer(rev, c->listening->post_accept_timeout);\n         ngx_http_quic_handshake(rev);\n         return;\n     }\n@@ -1103,7 +1102,6 @@\n     ngx_http_connection_t     *hc;\n     ngx_http_v3_srv_conf_t    *qscf;\n     ngx_http_ssl_srv_conf_t   *sscf;\n-    ngx_http_core_srv_conf_t  *cscf;\n \n     c = rev->data;\n     hc = c->data;\n@@ -1144,8 +1142,7 @@\n     if (rc == NGX_AGAIN) {\n \n         if (!rev->timer_set) {\n-            cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);\n-            ngx_add_timer(rev, cscf->client_header_timeout);\n+            ngx_add_timer(rev, c->listening->post_accept_timeout);\n         }\n \n         c->ssl->handler = ngx_http_ssl_handshake_handler;\n"
  },
  {
    "path": "nginx_with_quic_for_1.19.7_full.patch",
    "content": "Add HTTP3(QUIC) Support.\nAdd HTTP2 HPACK Encoding Support.\nAdd Dynamic TLS Record support.\n\nUsing: patch -p1 < nginx_with_quic_for_1.19.7_full.patch\nBased on cloudflare/quiche af1bbc0.\nRequires nginx 1.19.7 ~ 1.21.3 to patch.\n\ndiff --color -uNr a/auto/lib/conf b/auto/lib/conf\n--- a/auto/lib/conf\t2021-09-07 23:21:03.000000000 +0800\n+++ b/auto/lib/conf\t2021-09-09 19:04:29.388062635 +0800\n@@ -25,6 +25,10 @@\n     . auto/lib/openssl/conf\n fi\n \n+if [ $USE_QUICHE = YES ]; then\n+    . auto/lib/quiche/conf\n+fi\n+\n if [ $USE_ZLIB = YES ]; then\n     . auto/lib/zlib/conf\n fi\ndiff --color -uNr a/auto/lib/make b/auto/lib/make\n--- a/auto/lib/make\t2021-09-07 23:21:03.000000000 +0800\n+++ b/auto/lib/make\t2021-09-09 19:04:29.388062635 +0800\n@@ -11,6 +11,10 @@\n     . auto/lib/openssl/make\n fi\n \n+if [ $QUICHE != NONE -a $QUICHE != NO -a $QUICHE != YES ]; then\n+    . auto/lib/quiche/make\n+fi\n+\n if [ $ZLIB != NONE -a $ZLIB != NO -a $ZLIB != YES ]; then\n     . auto/lib/zlib/make\n fi\ndiff --color -uNr a/auto/lib/openssl/make b/auto/lib/openssl/make\n--- a/auto/lib/openssl/make\t2021-09-07 23:21:03.000000000 +0800\n+++ b/auto/lib/openssl/make\t2021-09-09 19:04:29.388062635 +0800\n@@ -49,11 +49,13 @@\n         cat << END                                            >> $NGX_MAKEFILE\n \n $OPENSSL/.openssl/include/openssl/ssl.h:\t$NGX_MAKEFILE\n-\tcd $OPENSSL \\\\\n-\t&& if [ -f Makefile ]; then \\$(MAKE) clean; fi \\\\\n-\t&& ./config --prefix=$ngx_prefix no-shared no-threads $OPENSSL_OPT \\\\\n-\t&& \\$(MAKE) \\\\\n-\t&& \\$(MAKE) install_sw LIBDIR=lib\n+\tmkdir -p $OPENSSL/build $OPENSSL/.openssl/lib $OPENSSL/.openssl/include/openssl \\\\\n+\t&& cd $OPENSSL/build \\\\\n+\t&& cmake -DCMAKE_C_FLAGS=\"$OPENSSL_OPT\" -DCMAKE_CXX_FLAGS=\"$OPENSSL_OPT\" .. \\\\\n+\t&& \\$(MAKE) VERBOSE=1 \\\\\n+\t&& cd .. \\\\\n+\t&& cp -r src/include/openssl/*.h .openssl/include/openssl \\\\\n+\t&& cp build/libssl.a build/libcrypto.a .openssl/lib\n \n END\n \ndiff --color -uNr a/auto/lib/quiche/conf b/auto/lib/quiche/conf\n--- a/auto/lib/quiche/conf\t1970-01-01 08:00:00.000000000 +0800\n+++ b/auto/lib/quiche/conf\t2021-09-09 19:04:29.388062635 +0800\n@@ -0,0 +1,23 @@\n+\n+# Copyright (C) Cloudflare, Inc.\n+\n+\n+if [ $QUICHE != NONE ]; then\n+\n+    have=NGX_QUIC . auto/have\n+\n+    QUICHE_BUILD_TARGET=\"release\"\n+\n+    if [ $NGX_DEBUG = YES ]; then\n+        QUICHE_BUILD_TARGET=\"debug\"\n+    fi\n+\n+    CORE_INCS=\"$CORE_INCS $QUICHE/include\"\n+    CORE_DEPS=\"$CORE_DEPS $QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a\"\n+    CORE_LIBS=\"$CORE_LIBS $QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a $NGX_LIBPTHREAD -lm\"\n+\n+    if [ \"$NGX_SYSTEM\" = \"Darwin\" ]; then\n+        CORE_LIBS+=\" -framework Security\"\n+    fi\n+\n+fi\ndiff --color -uNr a/auto/lib/quiche/make b/auto/lib/quiche/make\n--- a/auto/lib/quiche/make\t1970-01-01 08:00:00.000000000 +0800\n+++ b/auto/lib/quiche/make\t2021-09-09 19:04:29.389062662 +0800\n@@ -0,0 +1,23 @@\n+\n+# Copyright (C) Cloudflare, Inc.\n+\n+QUICHE_COMMON_FLAGS=\"--verbose --no-default-features --features ffi\"\n+\n+# Default is release build\n+QUICHE_BUILD_FLAGS=\"$QUICHE_COMMON_FLAGS --release\"\n+QUICHE_BUILD_TARGET=\"release\"\n+\n+if [ $NGX_DEBUG = YES ]; then\n+    QUICHE_BUILD_FLAGS=\"$QUICHE_COMMON_FLAGS\"\n+    QUICHE_BUILD_TARGET=\"debug\"\n+fi\n+\n+\n+cat << END                                                    >> $NGX_MAKEFILE\n+\n+$QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a: \\\\\n+\t\t$OPENSSL/.openssl/include/openssl/ssl.h \\\\\n+\t\t$NGX_MAKEFILE\n+\tcd $QUICHE && cargo build $QUICHE_BUILD_FLAGS $QUICHE_OPT\n+\n+END\ndiff --color -uNr a/auto/make b/auto/make\n--- a/auto/make\t2021-09-07 23:21:03.000000000 +0800\n+++ b/auto/make\t2021-09-09 19:04:29.389062662 +0800\n@@ -7,7 +7,8 @@\n \n mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \\\n          $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \\\n-         $NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/modules \\\n+         $NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/v3 \\\n+         $NGX_OBJS/src/http/modules \\\n          $NGX_OBJS/src/http/modules/perl \\\n          $NGX_OBJS/src/mail \\\n          $NGX_OBJS/src/stream \\\ndiff --color -uNr a/auto/modules b/auto/modules\n--- a/auto/modules\t2021-09-07 23:21:03.000000000 +0800\n+++ b/auto/modules\t2021-09-09 19:04:41.601382488 +0800\n@@ -119,6 +119,7 @@\n     #     ngx_http_header_filter\n     #     ngx_http_chunked_filter\n     #     ngx_http_v2_filter\n+    #     ngx_http_v3_filter\n     #     ngx_http_range_header_filter\n     #     ngx_http_gzip_filter\n     #     ngx_http_postpone_filter\n@@ -151,6 +152,7 @@\n                       ngx_http_header_filter_module \\\n                       ngx_http_chunked_filter_module \\\n                       ngx_http_v2_filter_module \\\n+                      ngx_http_v3_filter_module \\\n                       ngx_http_range_header_filter_module \\\n                       ngx_http_gzip_filter_module \\\n                       ngx_http_postpone_filter_module \\\n@@ -212,6 +214,17 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_V3 = YES ]; then\n+        ngx_module_name=ngx_http_v3_filter_module\n+        ngx_module_incs=\n+        ngx_module_deps=\n+        ngx_module_srcs=src/http/v3/ngx_http_v3_filter_module.c\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_V3\n+\n+        . auto/module\n+    fi\n+\n     if :; then\n         ngx_module_name=ngx_http_range_header_filter_module\n         ngx_module_incs=\n@@ -423,6 +436,28 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_V3 = YES ]; then\n+        USE_QUICHE=YES\n+        USE_OPENSSL=YES\n+        have=NGX_HTTP_V3 . auto/have\n+        have=NGX_HTTP_HEADERS . auto/have\n+\n+        ngx_module_name=ngx_http_v3_module\n+        ngx_module_incs=src/http/v3\n+        ngx_module_deps=\"src/http/v3/ngx_http_v3.h \\\n+                         src/http/v3/ngx_http_v3_module.h\"\n+        ngx_module_srcs=\"src/http/v3/ngx_http_v3.c \\\n+                         src/http/v3/ngx_http_v3_module.c\"\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_V3\n+\n+        . auto/module\n+    fi\n+\n+    if [ $HTTP_V2_HPACK_ENC = YES ]; then\n+        have=NGX_HTTP_V2_HPACK_ENC . auto/have\n+    fi\n+\n     if :; then\n         ngx_module_name=ngx_http_static_module\n         ngx_module_incs=\n@@ -1267,6 +1302,19 @@\n \n     . auto/module\n fi\n+\n+\n+if [ $USE_QUICHE = YES ]; then\n+    ngx_module_type=CORE\n+    ngx_module_name=ngx_quic_module\n+    ngx_module_incs=\n+    ngx_module_deps=src/event/ngx_event_quic.h\n+    ngx_module_srcs=src/event/ngx_event_quic.c\n+    ngx_module_libs=\n+    ngx_module_link=YES\n+\n+    . auto/module\n+fi\n \n \n if [ $USE_PCRE = YES ]; then\ndiff --color -uNr a/auto/options b/auto/options\n--- a/auto/options\t2021-09-07 23:21:03.000000000 +0800\n+++ b/auto/options\t2021-09-09 19:04:41.604382567 +0800\n@@ -59,6 +59,8 @@\n HTTP_GZIP=YES\n HTTP_SSL=NO\n HTTP_V2=NO\n+HTTP_V2_HPACK_ENC=NO\n+HTTP_V3=NO\n HTTP_SSI=YES\n HTTP_REALIP=NO\n HTTP_XSLT=NO\n@@ -150,6 +152,9 @@\n USE_OPENSSL=NO\n OPENSSL=NONE\n \n+USE_QUICHE=NO\n+QUICHE=NONE\n+\n USE_ZLIB=NO\n ZLIB=NONE\n ZLIB_OPT=\n@@ -227,6 +232,8 @@\n \n         --with-http_ssl_module)          HTTP_SSL=YES               ;;\n         --with-http_v2_module)           HTTP_V2=YES                ;;\n+        --with-http_v2_hpack_enc)        HTTP_V2_HPACK_ENC=YES      ;;\n+        --with-http_v3_module)           HTTP_V3=YES                ;;\n         --with-http_realip_module)       HTTP_REALIP=YES            ;;\n         --with-http_addition_module)     HTTP_ADDITION=YES          ;;\n         --with-http_xslt_module)         HTTP_XSLT=YES              ;;\n@@ -361,6 +368,9 @@\n         --with-openssl=*)                OPENSSL=\"$value\"           ;;\n         --with-openssl-opt=*)            OPENSSL_OPT=\"$value\"       ;;\n \n+        --with-quiche=*)                 QUICHE=\"$value\"           ;;\n+        --with-quiche-opt=*)             QUICHE_OPT=\"$value\"       ;;\n+\n         --with-md5=*)\n             NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n $0: warning: the \\\"--with-md5\\\" option is deprecated\"\n@@ -443,6 +453,8 @@\n \n   --with-http_ssl_module             enable ngx_http_ssl_module\n   --with-http_v2_module              enable ngx_http_v2_module\n+  --with-http_v2_hpack_enc           enable ngx_http_v2_hpack_enc\n+  --with-http_v3_module              enable ngx_http_v3_module\n   --with-http_realip_module          enable ngx_http_realip_module\n   --with-http_addition_module        enable ngx_http_addition_module\n   --with-http_xslt_module            enable ngx_http_xslt_module\ndiff --color -uNr a/src/core/ngx_connection.h b/src/core/ngx_connection.h\n--- a/src/core/ngx_connection.h\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/core/ngx_connection.h\t2021-09-09 19:04:29.390062688 +0800\n@@ -77,6 +77,9 @@\n     unsigned            deferred_accept:1;\n     unsigned            delete_deferred:1;\n     unsigned            add_deferred:1;\n+#if (NGX_QUIC)\n+    unsigned            quic:1;\n+#endif\n #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n     char               *accept_filter;\n #endif\n@@ -153,6 +156,10 @@\n \n     ngx_udp_connection_t  *udp;\n \n+#if (NGX_QUIC)\n+    ngx_quic_connection_t *quic;\n+#endif\n+\n     struct sockaddr    *local_sockaddr;\n     socklen_t           local_socklen;\n \ndiff --color -uNr a/src/core/ngx_core.h b/src/core/ngx_core.h\n--- a/src/core/ngx_core.h\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/core/ngx_core.h\t2021-09-09 19:04:29.390062688 +0800\n@@ -83,6 +83,9 @@\n #if (NGX_OPENSSL)\n #include <ngx_event_openssl.h>\n #endif\n+#if (NGX_QUIC)\n+#include <ngx_event_quic.h>\n+#endif\n #include <ngx_process_cycle.h>\n #include <ngx_conf_file.h>\n #include <ngx_module.h>\ndiff --color -uNr a/src/core/ngx_murmurhash.c b/src/core/ngx_murmurhash.c\n--- a/src/core/ngx_murmurhash.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/core/ngx_murmurhash.c\t2021-09-09 19:04:41.604382567 +0800\n@@ -50,3 +50,63 @@\n \n     return h;\n }\n+\n+\n+uint64_t\n+ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed)\n+{\n+    uint64_t  h, k;\n+\n+    h = seed ^ len;\n+\n+    while (len >= 8) {\n+        k  = data[0];\n+        k |= data[1] << 8;\n+        k |= data[2] << 16;\n+        k |= data[3] << 24;\n+        k |= (uint64_t)data[4] << 32;\n+        k |= (uint64_t)data[5] << 40;\n+        k |= (uint64_t)data[6] << 48;\n+        k |= (uint64_t)data[7] << 56;\n+\n+        k *= 0xc6a4a7935bd1e995ull;\n+        k ^= k >> 47;\n+        k *= 0xc6a4a7935bd1e995ull;\n+\n+        h ^= k;\n+        h *= 0xc6a4a7935bd1e995ull;\n+\n+        data += 8;\n+        len -= 8;\n+    }\n+\n+    switch (len) {\n+    case 7:\n+        h ^= (uint64_t)data[6] << 48;\n+        /* fall through */\n+    case 6:\n+        h ^= (uint64_t)data[5] << 40;\n+        /* fall through */\n+    case 5:\n+        h ^= (uint64_t)data[4] << 32;\n+        /* fall through */\n+    case 4:\n+        h ^= data[3] << 24;\n+        /* fall through */\n+    case 3:\n+        h ^= data[2] << 16;\n+        /* fall through */\n+    case 2:\n+        h ^= data[1] << 8;\n+        /* fall through */\n+    case 1:\n+        h ^= data[0];\n+        h *= 0xc6a4a7935bd1e995ull;\n+    }\n+\n+    h ^= h >> 47;\n+    h *= 0xc6a4a7935bd1e995ull;\n+    h ^= h >> 47;\n+\n+    return h;\n+}\ndiff --color -uNr a/src/core/ngx_murmurhash.h b/src/core/ngx_murmurhash.h\n--- a/src/core/ngx_murmurhash.h\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/core/ngx_murmurhash.h\t2021-09-09 19:04:41.605382593 +0800\n@@ -15,5 +15,7 @@\n \n uint32_t ngx_murmur_hash2(u_char *data, size_t len);\n \n+uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed);\n+\n \n #endif /* _NGX_MURMURHASH_H_INCLUDED_ */\ndiff --color -uNr a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c\n--- a/src/event/ngx_event_openssl.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/event/ngx_event_openssl.c\t2021-09-09 19:04:41.610382724 +0800\n@@ -1624,6 +1624,7 @@\n \n     sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);\n     sc->buffer_size = ssl->buffer_size;\n+    sc->dyn_rec = ssl->dyn_rec;\n \n     sc->session_ctx = ssl->ctx;\n \n@@ -2569,6 +2570,41 @@\n \n     for ( ;; ) {\n \n+        /* Dynamic record resizing:\n+           We want the initial records to fit into one TCP segment\n+           so we don't get TCP HoL blocking due to TCP Slow Start.\n+           A connection always starts with small records, but after\n+           a given amount of records sent, we make the records larger\n+           to reduce header overhead.\n+           After a connection has idled for a given timeout, begin\n+           the process from the start. The actual parameters are\n+           configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */\n+\n+        if (c->ssl->dyn_rec.timeout > 0 ) {\n+\n+            if (ngx_current_msec - c->ssl->dyn_rec_last_write >\n+                c->ssl->dyn_rec.timeout)\n+            {\n+                buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                c->ssl->dyn_rec_records_sent = 0;\n+\n+            } else {\n+                if (c->ssl->dyn_rec_records_sent >\n+                    c->ssl->dyn_rec.threshold * 2)\n+                {\n+                    buf->end = buf->start + c->ssl->buffer_size;\n+\n+                } else if (c->ssl->dyn_rec_records_sent >\n+                           c->ssl->dyn_rec.threshold)\n+                {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_hi;\n+\n+                } else {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                }\n+            }\n+        }\n+\n         while (in && buf->last < buf->end && send < limit) {\n             if (in->buf->last_buf || in->buf->flush) {\n                 flush = 1;\n@@ -2676,6 +2712,9 @@\n \n     if (n > 0) {\n \n+        c->ssl->dyn_rec_records_sent++;\n+        c->ssl->dyn_rec_last_write = ngx_current_msec;\n+\n         if (c->ssl->saved_read_handler) {\n \n             c->read->handler = c->ssl->saved_read_handler;\ndiff --color -uNr a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h\n--- a/src/event/ngx_event_openssl.h\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/event/ngx_event_openssl.h\t2021-09-09 19:04:41.611382750 +0800\n@@ -78,10 +78,19 @@\n typedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;\n \n \n+typedef struct {\n+    ngx_msec_t                  timeout;\n+    ngx_uint_t                  threshold;\n+    size_t                      size_lo;\n+    size_t                      size_hi;\n+} ngx_ssl_dyn_rec_t;\n+\n+\n struct ngx_ssl_s {\n     SSL_CTX                    *ctx;\n     ngx_log_t                  *log;\n     size_t                      buffer_size;\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n };\n \n \n@@ -118,6 +127,10 @@\n     unsigned                    in_ocsp:1;\n     unsigned                    early_preread:1;\n     unsigned                    write_blocked:1;\n+\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n+    ngx_msec_t                  dyn_rec_last_write;\n+    ngx_uint_t                  dyn_rec_records_sent;\n };\n \n \n@@ -127,7 +140,7 @@\n #define NGX_SSL_DFLT_BUILTIN_SCACHE  -5\n \n \n-#define NGX_SSL_MAX_SESSION_SIZE  4096\n+#define NGX_SSL_MAX_SESSION_SIZE  16384\n \n typedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;\n \ndiff --color -uNr a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c\n--- a/src/event/ngx_event_quic.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/event/ngx_event_quic.c\t2021-09-09 19:04:29.391062714 +0800\n@@ -0,0 +1,620 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_event.h>\n+\n+\n+/* Limit outgoing packets to 1200 bytes. This is the minimum value allowed. */\n+#define MAX_DATAGRAM_SIZE 1200\n+\n+/* errors */\n+#define NGX_QUIC_NO_ERROR  0x0\n+#define NGX_QUIC_INTERNAL_ERROR  0x1\n+\n+\n+static void ngx_quic_read_handler(ngx_event_t *ev);\n+static void ngx_quic_write_handler(ngx_event_t *ev);\n+\n+static void ngx_quic_set_timer(ngx_connection_t *c);\n+\n+static void ngx_quic_handshake_completed(ngx_connection_t *c);\n+\n+static void ngx_quic_shutdown_handler(ngx_event_t *ev);\n+\n+static void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t status);\n+static void ngx_quic_close_connection(ngx_connection_t *c);\n+\n+static ngx_int_t ngx_quic_send_udp_packet(ngx_connection_t *c, uint8_t *buf,\n+    size_t len);\n+\n+\n+static ngx_command_t  ngx_quic_commands[] = {\n+\n+    ngx_null_command\n+};\n+\n+\n+static ngx_core_module_t  ngx_quic_module_ctx = {\n+    ngx_string(\"quic\"),\n+    NULL,\n+    NULL\n+};\n+\n+\n+ngx_module_t  ngx_quic_module = {\n+    NGX_MODULE_V1,\n+    &ngx_quic_module_ctx,                  /* module context */\n+    ngx_quic_commands,                     /* module directives */\n+    NGX_CORE_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+ngx_int_t\n+ngx_quic_create_conf(ngx_quic_t *quic)\n+{\n+    quic->config = quiche_config_new(QUICHE_PROTOCOL_VERSION);\n+    if (quic->config == NULL) {\n+        ngx_log_error(NGX_LOG_EMERG, quic->log, 0, \"failed to create quic config\");\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_validate_initial(ngx_event_t *ev, u_char *buf, ssize_t buf_len)\n+{\n+    /* Check incoming packet type, if it's not Initial we shouldn't be here. */\n+    if (((buf[0] & 0x30) >> 4) != 0) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n+                       \"packet is not quic client initial\");\n+        return NGX_ERROR;\n+    }\n+\n+    /* Client Initial packets must be at least 1200 bytes. */\n+    if (buf_len < QUICHE_MIN_CLIENT_INITIAL_LEN) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n+                       \"quic initial packet is too short\");\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_create_connection(ngx_quic_t *quic, ngx_connection_t *c)\n+{\n+    int                     rc;\n+    u_char                 *buf;\n+    size_t                  buf_len;\n+    quiche_conn            *conn;\n+    static uint8_t          out[MAX_DATAGRAM_SIZE];\n+\n+    uint8_t                 pkt_type;\n+    uint32_t                pkt_version;\n+\n+    uint8_t                 scid[QUICHE_MAX_CONN_ID_LEN];\n+    size_t                  scid_len = sizeof(scid);\n+\n+    uint8_t                 dcid[QUICHE_MAX_CONN_ID_LEN];\n+    size_t                  dcid_len = sizeof(dcid);\n+\n+    uint8_t                 token[1];\n+    size_t                  token_len = sizeof(token);\n+\n+    ngx_quic_connection_t  *qc;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic init connection\");\n+\n+    /* Extract some fields from the client's Initial packet, which was saved\n+     * into c->buffer by ngx_event_recvmsg(). */\n+    buf = c->buffer->pos;\n+    buf_len = ngx_buf_size(c->buffer);\n+\n+    rc = quiche_header_info(buf, buf_len, QUICHE_MAX_CONN_ID_LEN,\n+                            &pkt_version, &pkt_type,\n+                            scid, &scid_len, dcid, &dcid_len,\n+                            token, &token_len);\n+    if (rc < 0) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"failed to parse quic header: %d\", rc);\n+        return NGX_ERROR;\n+    }\n+\n+    /* Version mismatch, do version negotiation. */\n+    if (!quiche_version_is_supported(pkt_version)) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic version negotiation\");\n+\n+        ssize_t written = quiche_negotiate_version(scid, scid_len,\n+                                                   dcid, dcid_len,\n+                                                   out, sizeof(out));\n+\n+        if (written < 0) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                          \"failed to create quic vneg packet: %d\", written);\n+            return NGX_ERROR;\n+        }\n+\n+        if (ngx_quic_send_udp_packet(c, out, written) == NGX_ERROR) {\n+            return NGX_ERROR;\n+        }\n+\n+        return NGX_DONE;\n+    }\n+\n+    /* Initialize source connection ID with some random bytes. */\n+    RAND_bytes(scid, sizeof(scid));\n+\n+#if (NGX_DEBUG)\n+    {\n+    uint8_t dcid_hex[QUICHE_MAX_CONN_ID_LEN * 2],\n+            scid_hex[QUICHE_MAX_CONN_ID_LEN * 2];\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+        \"new quic connection dcid:%*.s new_scid:%*.s\",\n+        ngx_hex_dump(dcid_hex, dcid, dcid_len) - dcid_hex, dcid_hex,\n+        ngx_hex_dump(scid_hex, scid, sizeof(scid)) - scid_hex, scid_hex);\n+    }\n+#endif\n+\n+    conn = quiche_conn_new_with_tls(scid, sizeof(scid), NULL, 0,\n+                                    c->sockaddr, c->socklen, quic->config,\n+                                    c->ssl->connection, true);\n+    if (conn == NULL) {\n+        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to create quic connection\");\n+        return NGX_ERROR;\n+    }\n+\n+    qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t));\n+    if (qc == NULL) {\n+        quiche_conn_free(conn);\n+        return NGX_ERROR;\n+    }\n+\n+    qc->handler = NULL;\n+\n+    qc->conn = conn;\n+\n+    c->quic = qc;\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_handshake(ngx_connection_t *c)\n+{\n+    u_char   *buf;\n+    size_t    buf_len;\n+    ssize_t   done;\n+\n+    quiche_recv_info recv_info = {\n+        c->sockaddr,\n+        c->socklen,\n+    };\n+\n+    /* Process the client's Initial packet, which was saved into c->buffer by\n+     * ngx_event_recvmsg(). */\n+    buf = c->buffer->pos;\n+    buf_len = ngx_buf_size(c->buffer);\n+\n+    done = quiche_conn_recv(c->quic->conn, buf, buf_len, &recv_info);\n+\n+    if ((done < 0) && (done != QUICHE_ERR_DONE)) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                      \"failed to process quic packet: %d\", done);\n+        return NGX_ERROR;\n+    }\n+\n+    c->read->handler = ngx_quic_read_handler;\n+    c->write->handler = ngx_quic_write_handler;\n+\n+    ngx_post_event(c->write, &ngx_posted_events);\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static void\n+ngx_quic_read_handler(ngx_event_t *rev)\n+{\n+    int                n;\n+    static uint8_t     buf[65535];\n+    ngx_connection_t  *c;\n+\n+    c = rev->data;\n+\n+    c->log->action = \"reading QUIC packets\";\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic read handler\");\n+\n+    if (rev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic connection timed out\");\n+\n+        if (c->quic->handler != NULL) {\n+            c->quic->handler(c);\n+        }\n+\n+        return;\n+    }\n+\n+    for (;;) {\n+        n = c->recv(c, buf, sizeof(buf));\n+        if (n == NGX_AGAIN) {\n+            break;\n+        }\n+\n+        if (n == NGX_ERROR) {\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+\n+        quiche_recv_info recv_info = {\n+            c->sockaddr,\n+            c->socklen,\n+        };\n+\n+        ssize_t done = quiche_conn_recv(c->quic->conn, buf, n, &recv_info);\n+\n+        if (done == QUICHE_ERR_DONE) {\n+            break;\n+        }\n+\n+        if (done < 0) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"failed to process quic packet: %d\", done);\n+\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+    }\n+\n+    if (quiche_conn_is_in_early_data(c->quic->conn) ||\n+            quiche_conn_is_established(c->quic->conn)) {\n+        if (!c->ssl->handshaked) {\n+            ngx_quic_handshake_completed(c);\n+        }\n+\n+        if ((c->quic == NULL) || (c->quic->handler == NULL)) {\n+            return;\n+        }\n+\n+        /* Notify application layer that there might be stream data to read. */\n+        c->quic->handler(c);\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic done reading\");\n+\n+    ngx_post_event(c->write, &ngx_posted_events);\n+}\n+\n+\n+static void\n+ngx_quic_write_handler(ngx_event_t *wev)\n+{\n+    ngx_connection_t   *c;\n+    quiche_send_info    send_info;\n+    static uint8_t      out[MAX_DATAGRAM_SIZE];\n+\n+    c = wev->data;\n+\n+    c->log->action = \"writing QUIC packets\";\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic write handler\");\n+\n+    if (wev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic alarm fired\");\n+\n+        quiche_conn_on_timeout(c->quic->conn);\n+    }\n+\n+    if (quiche_conn_is_closed(c->quic->conn)) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic connection is closed\");\n+\n+        ngx_quic_finalize_connection(c, NGX_QUIC_NO_ERROR);\n+        return;\n+    }\n+\n+    for (;;) {\n+        ssize_t written = quiche_conn_send(c->quic->conn, out, sizeof(out),\n+                                           &send_info);\n+\n+        if (written == QUICHE_ERR_DONE) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic done writing\");\n+            break;\n+        }\n+\n+        if (written < 0) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"failed to create quic packet: %d\", written);\n+\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+\n+        int rc = ngx_quic_send_udp_packet(c, out, written);\n+\n+        if (rc == NGX_AGAIN) {\n+            break;\n+        }\n+\n+        if (rc == NGX_ERROR) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"failed to send quic packet\");\n+\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+    }\n+\n+    ngx_quic_set_timer(c);\n+}\n+\n+\n+static void\n+ngx_quic_set_timer(ngx_connection_t *c)\n+{\n+    uint64_t      expiry;\n+    ngx_event_t  *wev;\n+\n+    wev = c->write;\n+\n+    expiry = quiche_conn_timeout_as_millis(c->quic->conn);\n+    expiry = ngx_max(expiry, 1);\n+\n+    if (wev->timer_set) {\n+        ngx_del_timer(wev);\n+    }\n+\n+    /* quiche_conn_timeout_as_millis() will return UINT64_MAX when the timer\n+     * should be unset (this would be equvalent to returning Option::None in\n+     * Rust). To avoid overflow we need to explicitly check for this value. */\n+    if (expiry != UINT64_MAX) {\n+        ngx_add_timer(wev, (ngx_msec_t)expiry);\n+    }\n+}\n+\n+\n+static void\n+ngx_quic_handshake_completed(ngx_connection_t *c)\n+{\n+#if (NGX_DEBUG)\n+    {\n+    char         buf[129], *s, *d;\n+#if OPENSSL_VERSION_NUMBER >= 0x10000000L\n+    const\n+#endif\n+    SSL_CIPHER  *cipher;\n+\n+    cipher = SSL_get_current_cipher(c->ssl->connection);\n+\n+    if (cipher) {\n+        SSL_CIPHER_description(cipher, &buf[1], 128);\n+\n+        for (s = &buf[1], d = buf; *s; s++) {\n+            if (*s == ' ' && *d == ' ') {\n+                continue;\n+            }\n+\n+            if (*s == LF || *s == CR) {\n+                continue;\n+            }\n+\n+            *++d = *s;\n+        }\n+\n+        if (*d != ' ') {\n+            d++;\n+        }\n+\n+        *d = '\\0';\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"QUIC: %s, cipher: \\\"%s\\\"\",\n+                       SSL_get_version(c->ssl->connection), &buf[1]);\n+\n+        if (SSL_session_reused(c->ssl->connection)) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"quic reused session\");\n+        }\n+\n+    } else {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic no shared ciphers\");\n+    }\n+    }\n+#endif\n+\n+    ngx_del_timer(c->read);\n+\n+    c->ssl->handshaked = 1;\n+\n+    /* Notify application layer that the handshake is complete. */\n+    c->ssl->handler(c);\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_shutdown(ngx_connection_t *c)\n+{\n+    ssize_t           written;\n+    quiche_send_info  send_info;\n+    static uint8_t    out[MAX_DATAGRAM_SIZE];\n+\n+    /* Connection is closed, free memory. */\n+    if (quiche_conn_is_closed(c->quic->conn)) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"free quic connection\");\n+\n+        quiche_conn_free(c->quic->conn);\n+\n+        c->quic = NULL;\n+        c->ssl = NULL;\n+\n+        return NGX_OK;\n+    }\n+\n+    /* We can't free the connection state yet, as we need to wait for the\n+     * draining timeout to expire.\n+     *\n+     * Setup event handlers such that we will try again when that happens (or\n+     * when another event is triggered). */\n+    c->read->handler = ngx_quic_shutdown_handler;\n+    c->write->handler = ngx_quic_shutdown_handler;\n+\n+    /* Try sending a packet in order to flush pending frames (CONNECTION_CLOSE\n+     * for example), but ignore errors as we are already closing the connection\n+     * anyway. */\n+    written = quiche_conn_send(c->quic->conn, out, sizeof(out), &send_info);\n+\n+    if (written > 0) {\n+        ngx_quic_send_udp_packet(c, out, written);\n+    }\n+\n+    ngx_quic_set_timer(c);\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static void\n+ngx_quic_shutdown_handler(ngx_event_t *ev)\n+{\n+    ngx_connection_t           *c;\n+    ngx_connection_handler_pt   handler;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"quic shutdown handler\");\n+\n+    c = ev->data;\n+    handler = c->quic->handler;\n+\n+    if (ev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic alarm fired\");\n+\n+        quiche_conn_on_timeout(c->quic->conn);\n+    }\n+\n+    if (ngx_quic_shutdown(c) == NGX_AGAIN) {\n+        return;\n+    }\n+\n+    handler(c);\n+}\n+\n+\n+static void\n+ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t status)\n+{\n+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                   \"finalize quic connection: %d\", c->fd);\n+\n+    c->error = 1;\n+\n+    if (quiche_conn_is_closed(c->quic->conn)) {\n+        c->close = 1;\n+    }\n+\n+    quiche_conn_close(c->quic->conn, false, status, NULL, 0);\n+\n+    /* Notify the application layer that the connection is in an error\n+     * state and will be closed. */\n+    if (c->quic->handler != NULL) {\n+        c->quic->handler(c);\n+        return;\n+    }\n+\n+    ngx_quic_close_connection(c);\n+}\n+\n+\n+static void\n+ngx_quic_close_connection(ngx_connection_t *c)\n+{\n+    ngx_pool_t  *pool;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                   \"close quic connection: %d\", c->fd);\n+\n+    if (c->quic) {\n+        if (ngx_quic_shutdown(c) == NGX_AGAIN) {\n+            c->quic->handler = ngx_quic_close_connection;\n+            return;\n+        }\n+    }\n+\n+#if (NGX_STAT_STUB)\n+    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);\n+#endif\n+\n+    c->destroyed = 1;\n+\n+    pool = c->pool;\n+\n+    ngx_close_connection(c);\n+\n+    ngx_destroy_pool(pool);\n+}\n+\n+\n+void\n+ngx_quic_cleanup_ctx(void *data)\n+{\n+    ngx_quic_t  *quic = data;\n+\n+    quiche_config_free(quic->config);\n+}\n+\n+\n+static ngx_int_t\n+ngx_quic_send_udp_packet(ngx_connection_t *c, uint8_t *buf, size_t len)\n+{\n+    ngx_buf_t     out_buf = {0};\n+    ngx_chain_t   out_chain = {0};\n+    ngx_chain_t  *cl;\n+\n+    /* The send_chain() API takes an ngx_chain_t parameter instead of a simple\n+     * buffer, so we need to initialize the chain such that it contains only a\n+     * single buffer.\n+     *\n+     * The c->send_chain() call is required (instead of just c->send()) because\n+     * it uses the sendmsg(2) syscall (instead of sendto(2)), which allows us to\n+     * specify the correct source IP address for the connection. */\n+\n+    out_buf.start = out_buf.pos = buf;\n+    out_buf.end = out_buf.last = buf + len;\n+    out_buf.memory = 1;\n+    out_buf.flush = 1;\n+\n+    out_chain.buf = &out_buf;\n+    out_chain.next = NULL;\n+\n+    c->write->ready = 1;\n+\n+    cl = c->send_chain(c, &out_chain, 0);\n+\n+    if (cl != NULL) {\n+        return NGX_AGAIN;\n+    }\n+\n+    if (cl == NGX_CHAIN_ERROR) {\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\ndiff --color -uNr a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h\n--- a/src/event/ngx_event_quic.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/event/ngx_event_quic.h\t2021-09-09 19:04:29.391062714 +0800\n@@ -0,0 +1,49 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#ifndef _NGX_EVENT_QUIC_H_INCLUDED_\n+#define _NGX_EVENT_QUIC_H_INCLUDED_\n+\n+\n+#include <stdbool.h>\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+\n+#include <quiche.h>\n+\n+typedef struct ngx_quic_s              ngx_quic_t;\n+typedef struct ngx_quic_connection_s   ngx_quic_connection_t;\n+\n+struct ngx_quic_s {\n+    quiche_config              *config;\n+    ngx_log_t                  *log;\n+};\n+\n+struct ngx_quic_connection_s {\n+    quiche_conn                *conn;\n+\n+    ngx_connection_handler_pt   handler;\n+};\n+\n+\n+ngx_int_t ngx_quic_create_conf(ngx_quic_t *quic);\n+\n+ngx_int_t ngx_quic_validate_initial(ngx_event_t *ev, u_char *buf,\n+    ssize_t buf_len);\n+\n+ngx_int_t ngx_quic_create_connection(ngx_quic_t *quic, ngx_connection_t *c);\n+\n+ngx_int_t ngx_quic_create_ssl_connection(ngx_ssl_t *ssl, ngx_connection_t *c,\n+    ngx_uint_t flags);\n+\n+ngx_int_t ngx_quic_handshake(ngx_connection_t *c);\n+\n+ngx_int_t ngx_quic_shutdown(ngx_connection_t *c);\n+\n+void ngx_quic_cleanup_ctx(void *data);\n+\n+#endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */\ndiff --color -uNr a/src/event/ngx_event_udp.c b/src/event/ngx_event_udp.c\n--- a/src/event/ngx_event_udp.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/event/ngx_event_udp.c\t2021-09-09 19:04:29.391062714 +0800\n@@ -276,6 +276,14 @@\n         (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);\n #endif\n \n+#if (NGX_QUIC)\n+        if (ls->quic) {\n+            if (ngx_quic_validate_initial(ev, buffer, n) != NGX_OK) {\n+                goto next;\n+            }\n+        }\n+#endif\n+\n         ngx_accept_disabled = ngx_cycle->connection_n / 8\n                               - ngx_cycle->free_connection_n;\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c\n--- a/src/http/modules/ngx_http_ssl_module.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.c\t2021-09-09 19:04:41.615382855 +0800\n@@ -301,6 +301,41 @@\n       offsetof(ngx_http_ssl_srv_conf_t, reject_handshake),\n       NULL },\n \n+    { ngx_string(\"ssl_dyn_rec_enable\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_flag_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_lo\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_hi\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_threshold\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold),\n+      NULL },\n+\n       ngx_null_command\n };\n \n@@ -423,7 +458,7 @@\n #if (NGX_DEBUG)\n     unsigned int            i;\n #endif\n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_V3)\n     ngx_http_connection_t  *hc;\n #endif\n #if (NGX_HTTP_V2 || NGX_DEBUG)\n@@ -440,9 +475,11 @@\n     }\n #endif\n \n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_V3)\n     hc = c->data;\n+#endif\n \n+#if (NGX_HTTP_V2)\n     if (hc->addr_conf->http2) {\n         srv =\n            (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n@@ -450,6 +487,13 @@\n \n     } else\n #endif\n+#if (NGX_HTTP_V3)\n+    if (hc->addr_conf->quic) {\n+        srv = (unsigned char *) QUICHE_H3_APPLICATION_PROTOCOL;\n+        srvlen = sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1;\n+\n+    } else\n+#endif\n     {\n         srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;\n         srvlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;\n@@ -637,6 +681,11 @@\n     sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR;\n     sscf->stapling = NGX_CONF_UNSET;\n     sscf->stapling_verify = NGX_CONF_UNSET;\n+    sscf->dyn_rec_enable = NGX_CONF_UNSET;\n+    sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC;\n+    sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT;\n \n     return sscf;\n }\n@@ -712,6 +761,20 @@\n     ngx_conf_merge_str_value(conf->stapling_responder,\n                          prev->stapling_responder, \"\");\n \n+    ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0);\n+    ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout,\n+                             1000);\n+    /* Default sizes for the dynamic record sizes are defined to fit maximal\n+       TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi:\n+       1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo,\n+                             1369);\n+    /* 4229 = (1500 - 40 - 20 - 10) * 3  - 61 */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi,\n+                             4229);\n+    ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold,\n+                             40);\n+\n     conf->ssl.log = cf->log;\n \n     if (conf->enable) {\n@@ -943,6 +1006,28 @@\n         return NGX_CONF_ERROR;\n     }\n \n+    if (conf->dyn_rec_enable) {\n+        conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout;\n+        conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold;\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_lo) {\n+            conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_lo = conf->buffer_size;\n+        }\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_hi) {\n+            conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_hi = conf->buffer_size;\n+        }\n+\n+    } else {\n+        conf->ssl.dyn_rec.timeout = 0;\n+    }\n+\n     return NGX_CONF_OK;\n }\n \ndiff --color -uNr a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h\n--- a/src/http/modules/ngx_http_ssl_module.h\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.h\t2021-09-09 19:04:41.616382881 +0800\n@@ -67,6 +67,12 @@\n \n     u_char                         *file;\n     ngx_uint_t                      line;\n+\n+    ngx_flag_t                      dyn_rec_enable;\n+    ngx_msec_t                      dyn_rec_timeout;\n+    size_t                          dyn_rec_size_lo;\n+    size_t                          dyn_rec_size_hi;\n+    ngx_uint_t                      dyn_rec_threshold;\n } ngx_http_ssl_srv_conf_t;\n \n \ndiff --color -uNr a/src/http/ngx_http.c b/src/http/ngx_http.c\n--- a/src/http/ngx_http.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/ngx_http.c\t2021-09-09 19:04:29.392062740 +0800\n@@ -1178,6 +1178,7 @@\n ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,\n     ngx_http_listen_opt_t *lsopt)\n {\n+    int                         t;\n     in_port_t                   p;\n     ngx_uint_t                  i;\n     struct sockaddr            *sa;\n@@ -1196,11 +1197,13 @@\n \n     sa = lsopt->sockaddr;\n     p = ngx_inet_get_port(sa);\n+    t = lsopt->quic ? SOCK_DGRAM : SOCK_STREAM;\n \n     port = cmcf->ports->elts;\n     for (i = 0; i < cmcf->ports->nelts; i++) {\n \n-        if (p != port[i].port || sa->sa_family != port[i].family) {\n+        if (p != port[i].port || sa->sa_family != port[i].family\n+             || t != port[i].type) {\n             continue;\n         }\n \n@@ -1219,6 +1222,7 @@\n     port->family = sa->sa_family;\n     port->port = p;\n     port->addrs.elts = NULL;\n+    port->type = t;\n \n     return ngx_http_add_address(cf, cscf, port, lsopt);\n }\n@@ -1236,6 +1240,9 @@\n #if (NGX_HTTP_V2)\n     ngx_uint_t             http2;\n #endif\n+#if (NGX_HTTP_V3)\n+    ngx_uint_t             quic;\n+#endif\n \n     /*\n      * we cannot compare whole sockaddr struct's as kernel\n@@ -1271,6 +1278,9 @@\n #if (NGX_HTTP_V2)\n         http2 = lsopt->http2 || addr[i].opt.http2;\n #endif\n+#if (NGX_HTTP_V3)\n+        quic = lsopt->quic || addr[i].opt.quic;\n+#endif\n \n         if (lsopt->set) {\n \n@@ -1307,6 +1317,9 @@\n #if (NGX_HTTP_V2)\n         addr[i].opt.http2 = http2;\n #endif\n+#if (NGX_HTTP_V3)\n+        addr[i].opt.quic = quic;\n+#endif\n \n         return NGX_OK;\n     }\n@@ -1725,6 +1738,12 @@\n             break;\n         }\n \n+#if (NGX_HTTP_V3)\n+        if (addr[i].opt.quic) {\n+            ls->type = SOCK_DGRAM;\n+        }\n+#endif\n+\n         addr++;\n         last--;\n     }\n@@ -1806,6 +1825,12 @@\n     ls->reuseport = addr->opt.reuseport;\n #endif\n \n+#if (NGX_HTTP_V3)\n+    ls->quic = addr->opt.quic;\n+\n+    ls->wildcard = addr->opt.wildcard;\n+#endif\n+\n     return ls;\n }\n \n@@ -1839,6 +1864,9 @@\n         addrs[i].conf.http2 = addr[i].opt.http2;\n #endif\n         addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n+#if (NGX_HTTP_V3)\n+        addrs[i].conf.quic = addr[i].opt.quic;\n+#endif\n \n         if (addr[i].hash.buckets == NULL\n             && (addr[i].wc_head == NULL\n@@ -1904,6 +1932,9 @@\n         addrs6[i].conf.http2 = addr[i].opt.http2;\n #endif\n         addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n+#if (NGX_HTTP_V3)\n+        addrs6[i].conf.quic = addr[i].opt.quic;\n+#endif\n \n         if (addr[i].hash.buckets == NULL\n             && (addr[i].wc_head == NULL\ndiff --color -uNr a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c\n--- a/src/http/ngx_http_core_module.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/ngx_http_core_module.c\t2021-09-09 19:04:29.393062766 +0800\n@@ -4191,6 +4191,13 @@\n             continue;\n         }\n \n+#if (NGX_HTTP_V3)\n+        if (ngx_strcmp(value[n].data, \"quic\") == 0) {\n+            lsopt.quic = 1;\n+            continue;\n+        }\n+#endif\n+\n         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                            \"invalid parameter \\\"%V\\\"\", &value[n]);\n         return NGX_CONF_ERROR;\ndiff --color -uNr a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h\n--- a/src/http/ngx_http_core_module.h\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/ngx_http_core_module.h\t2021-09-09 19:04:29.393062766 +0800\n@@ -82,6 +82,7 @@\n     unsigned                   reuseport:1;\n     unsigned                   so_keepalive:2;\n     unsigned                   proxy_protocol:1;\n+    unsigned                   quic:1;\n \n     int                        backlog;\n     int                        rcvbuf;\n@@ -238,6 +239,7 @@\n     unsigned                   ssl:1;\n     unsigned                   http2:1;\n     unsigned                   proxy_protocol:1;\n+    unsigned                   quic:1;\n };\n \n \n@@ -268,6 +270,7 @@\n     ngx_int_t                  family;\n     in_port_t                  port;\n     ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */\n+    ngx_int_t                  type;\n } ngx_http_conf_port_t;\n \n \ndiff --color -uNr a/src/http/ngx_http.h b/src/http/ngx_http.h\n--- a/src/http/ngx_http.h\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/ngx_http.h\t2021-09-09 19:04:29.392062740 +0800\n@@ -20,6 +20,7 @@\n typedef struct ngx_http_log_ctx_s     ngx_http_log_ctx_t;\n typedef struct ngx_http_chunked_s     ngx_http_chunked_t;\n typedef struct ngx_http_v2_stream_s   ngx_http_v2_stream_t;\n+typedef struct ngx_http_v3_stream_s   ngx_http_v3_stream_t;\n \n typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,\n     ngx_table_elt_t *h, ngx_uint_t offset);\n@@ -38,6 +39,9 @@\n #if (NGX_HTTP_V2)\n #include <ngx_http_v2.h>\n #endif\n+#if (NGX_HTTP_V3)\n+#include <ngx_http_v3.h>\n+#endif\n #if (NGX_HTTP_CACHE)\n #include <ngx_http_cache.h>\n #endif\ndiff --color -uNr a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c\n--- a/src/http/ngx_http_request_body.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/ngx_http_request_body.c\t2021-09-09 19:04:29.395062819 +0800\n@@ -314,6 +314,12 @@\n                             ngx_del_timer(c->read);\n                         }\n \n+#if (NGX_HTTP_V3)\n+                        if (r->qstream) {\n+                            return NGX_AGAIN;\n+                        }\n+#endif\n+\n                         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                             return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                         }\n@@ -418,6 +424,12 @@\n             clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n             ngx_add_timer(c->read, clcf->client_body_timeout);\n \n+#if (NGX_HTTP_V3)\n+            if (r->qstream) {\n+                return NGX_AGAIN;\n+            }\n+#endif\n+\n             if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                 return NGX_HTTP_INTERNAL_SERVER_ERROR;\n             }\n@@ -625,6 +637,17 @@\n     }\n #endif\n \n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        r->qstream->skip_data = 1;\n+\n+        /* disable stream read to avoid pointless data events */\n+        ngx_http_v3_stop_stream_read(r->qstream, 0);\n+\n+        return NGX_OK;\n+    }\n+#endif\n+\n     if (ngx_http_test_expect(r) != NGX_OK) {\n         return NGX_HTTP_INTERNAL_SERVER_ERROR;\n     }\n@@ -921,6 +944,9 @@\n #if (NGX_HTTP_V2)\n         || r->stream != NULL\n #endif\n+#if (NGX_HTTP_V3)\n+        || r->qstream != NULL\n+#endif\n        )\n     {\n         return NGX_OK;\n@@ -960,6 +986,13 @@\n static ngx_int_t\n ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n {\n+\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        return ngx_http_v3_request_body_filter(r, in);\n+    }\n+#endif\n+\n     if (r->headers_in.chunked) {\n         return ngx_http_request_body_chunked_filter(r, in);\n \ndiff --color -uNr a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c\n--- a/src/http/ngx_http_request.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/ngx_http_request.c\t2021-09-09 19:04:43.721438005 +0800\n@@ -64,6 +64,10 @@\n static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);\n #endif\n \n+#if (NGX_HTTP_V3)\n+static void ngx_http_quic_handshake(ngx_event_t *rev);\n+#endif\n+\n \n static char *ngx_http_client_errors[] = {\n \n@@ -350,6 +354,20 @@\n         c->log->action = \"reading PROXY protocol\";\n     }\n \n+#if (NGX_HTTP_V3)\n+    if (hc->addr_conf->quic) {\n+        hc->quic = 1;\n+        c->log->action = \"QUIC handshaking\";\n+\n+        /* We already have a UDP packet in the connection buffer, so we don't\n+         * need to wait for another read event to kick-off the handshake. */\n+        cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);\n+        ngx_add_timer(rev, cscf->client_header_timeout);\n+        ngx_http_quic_handshake(rev);\n+        return;\n+    }\n+#endif\n+\n     if (rev->ready) {\n         /* the deferred accept(), iocp */\n \n@@ -805,7 +823,7 @@\n \n         c->ssl->no_wait_shutdown = 1;\n \n-#if (NGX_HTTP_V2                                                              \\\n+#if ((NGX_HTTP_V2 || NGX_HTTP_V3)                                             \\\n      && (defined TLSEXT_TYPE_application_layer_protocol_negotiation           \\\n          || defined TLSEXT_TYPE_next_proto_neg))\n         {\n@@ -815,7 +833,7 @@\n \n         hc = c->data;\n \n-        if (hc->addr_conf->http2) {\n+        if (hc->addr_conf->http2 || hc->addr_conf->quic) {\n \n #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n             SSL_get0_alpn_selected(c->ssl->connection, &data, &len);\n@@ -830,11 +848,29 @@\n             SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);\n #endif\n \n+        }\n+\n+#if (NGX_HTTP_V2)\n+        if (hc->addr_conf->http2) {\n             if (len == 2 && data[0] == 'h' && data[1] == '2') {\n                 ngx_http_v2_init(c->read);\n                 return;\n             }\n         }\n+#endif\n+\n+#if (NGX_HTTP_V3)\n+        if (hc->addr_conf->quic) {\n+            if (len >= 2 && data[0] == 'h' && data[1] == '3') {\n+                ngx_http_v3_init(c->read);\n+                return;\n+            }\n+\n+            ngx_http_close_connection(c);\n+            return;\n+        }\n+#endif\n+\n         }\n #endif\n \n@@ -1059,6 +1095,70 @@\n \n #endif\n \n+#if (NGX_HTTP_V3)\n+\n+static void\n+ngx_http_quic_handshake(ngx_event_t *rev)\n+{\n+    ngx_int_t                  rc;\n+    ngx_connection_t          *c;\n+    ngx_http_connection_t     *hc;\n+    ngx_http_v3_srv_conf_t    *qscf;\n+    ngx_http_ssl_srv_conf_t   *sscf;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    c = rev->data;\n+    hc = c->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,\n+                   \"http check quic handshake\");\n+\n+    if (rev->timedout) {\n+        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    if (c->close) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, \"https quic handshake\");\n+\n+    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,\n+                                        ngx_http_ssl_module);\n+\n+    if (ngx_ssl_create_connection(&sscf->ssl, c, 0) != NGX_OK) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    qscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);\n+\n+    if (ngx_quic_create_connection(&qscf->quic, c) != NGX_OK) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    rc = ngx_quic_handshake(c);\n+\n+    if (rc == NGX_AGAIN) {\n+\n+        if (!rev->timer_set) {\n+            cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);\n+            ngx_add_timer(rev, cscf->client_header_timeout);\n+        }\n+\n+        c->ssl->handler = ngx_http_ssl_handshake_handler;\n+        return;\n+    }\n+\n+    ngx_http_ssl_handshake_handler(c);\n+}\n+\n+#endif\n+\n \n static void\n ngx_http_process_request_line(ngx_event_t *rev)\n@@ -2748,6 +2848,13 @@\n     }\n #endif\n \n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        ngx_http_close_request(r, 0);\n+        return;\n+    }\n+#endif\n+\n     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n \n     if (r->main->count != 1) {\n@@ -2962,6 +3069,19 @@\n \n #endif\n \n+#if (NGX_HTTP_V3)\n+\n+    if (r->qstream) {\n+        if (c->error) {\n+            err = 0;\n+            goto closed;\n+        }\n+\n+        return;\n+    }\n+\n+#endif\n+\n #if (NGX_HAVE_KQUEUE)\n \n     if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n@@ -3654,7 +3774,15 @@\n     }\n #endif\n \n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        ngx_http_v3_close_stream(r->qstream, rc);\n+        return;\n+    }\n+#endif\n+\n     ngx_http_free_request(r, rc);\n+\n     ngx_http_close_connection(c);\n }\n \n@@ -3775,6 +3903,17 @@\n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                    \"close http connection: %d\", c->fd);\n \n+#if (NGX_HTTP_V3)\n+\n+    if (c->quic) {\n+        if (ngx_quic_shutdown(c) == NGX_AGAIN) {\n+            c->quic->handler = ngx_http_close_connection;\n+            return;\n+        }\n+    }\n+\n+#endif\n+\n #if (NGX_HTTP_SSL)\n \n     if (c->ssl) {\ndiff --color -uNr a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h\n--- a/src/http/ngx_http_request.h\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/ngx_http_request.h\t2021-09-09 19:04:29.395062819 +0800\n@@ -24,6 +24,7 @@\n #define NGX_HTTP_VERSION_10                1000\n #define NGX_HTTP_VERSION_11                1001\n #define NGX_HTTP_VERSION_20                2000\n+#define NGX_HTTP_VERSION_3                 3000\n \n #define NGX_HTTP_UNKNOWN                   0x00000001\n #define NGX_HTTP_GET                       0x00000002\n@@ -327,6 +328,7 @@\n     ngx_chain_t                      *free;\n \n     unsigned                          ssl:1;\n+    unsigned                          quic:1;\n     unsigned                          proxy_protocol:1;\n } ngx_http_connection_t;\n \n@@ -449,6 +451,7 @@\n \n     ngx_http_connection_t            *http_connection;\n     ngx_http_v2_stream_t             *stream;\n+    ngx_http_v3_stream_t             *qstream;\n \n     ngx_http_log_handler_pt           log_handler;\n \ndiff --color -uNr a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c\n--- a/src/http/ngx_http_upstream.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/ngx_http_upstream.c\t2021-09-09 19:04:29.396062845 +0800\n@@ -525,6 +525,13 @@\n     }\n #endif\n \n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        ngx_http_upstream_init_request(r);\n+        return;\n+    }\n+#endif\n+\n     if (c->read->timer_set) {\n         ngx_del_timer(c->read);\n     }\n@@ -1357,6 +1364,12 @@\n         return;\n     }\n #endif\n+\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        return;\n+    }\n+#endif\n \n #if (NGX_HAVE_KQUEUE)\n \ndiff --color -uNr a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c\n--- a/src/http/v2/ngx_http_v2.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.c\t2021-09-09 19:04:41.619382959 +0800\n@@ -274,6 +274,8 @@\n \n     h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n \n+    h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE;\n+\n     h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n \n     h2c->concurrent_pushes = h2scf->concurrent_pushes;\n@@ -2283,6 +2285,14 @@\n         case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:\n \n             h2c->table_update = 1;\n+\n+            if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+                h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE;\n+            } else {\n+                h2c->max_hpack_table_size = value;\n+            }\n+\n+            h2c->indicate_resize = 1;\n             break;\n \n         default:\ndiff --color -uNr a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c\n--- a/src/http/v2/ngx_http_v2_encode.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_encode.c\t2021-09-09 19:04:41.619382959 +0800\n@@ -10,7 +10,7 @@\n #include <ngx_http.h>\n \n \n-static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n+u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n     ngx_uint_t value);\n \n \n@@ -40,7 +40,7 @@\n }\n \n \n-static u_char *\n+u_char *\n ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)\n {\n     if (value < prefix) {\ndiff --color -uNr a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c\n--- a/src/http/v2/ngx_http_v2_filter_module.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_filter_module.c\t2021-09-09 19:04:41.621383012 +0800\n@@ -23,10 +23,53 @@\n #define ngx_http_v2_literal_size(h)                                           \\\n     (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)\n \n+#define ngx_http_v2_indexed(i)      (128 + (i))\n+#define ngx_http_v2_inc_indexed(i)  (64 + (i))\n+\n+#define NGX_HTTP_V2_ENCODE_RAW            0\n+#define NGX_HTTP_V2_ENCODE_HUFF           0x80\n+\n+#define NGX_HTTP_V2_AUTHORITY_INDEX       1\n+#define NGX_HTTP_V2_METHOD_GET_INDEX      2\n+#define NGX_HTTP_V2_PATH_INDEX            4\n+\n+#define NGX_HTTP_V2_SCHEME_HTTP_INDEX     6\n+#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX    7\n+\n+#define NGX_HTTP_V2_STATUS_INDEX          8\n+#define NGX_HTTP_V2_STATUS_200_INDEX      8\n+#define NGX_HTTP_V2_STATUS_204_INDEX      9\n+#define NGX_HTTP_V2_STATUS_206_INDEX      10\n+#define NGX_HTTP_V2_STATUS_304_INDEX      11\n+#define NGX_HTTP_V2_STATUS_400_INDEX      12\n+#define NGX_HTTP_V2_STATUS_404_INDEX      13\n+#define NGX_HTTP_V2_STATUS_500_INDEX      14\n+\n+#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16\n+#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17\n+#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX  28\n+#define NGX_HTTP_V2_CONTENT_TYPE_INDEX    31\n+#define NGX_HTTP_V2_DATE_INDEX            33\n+#define NGX_HTTP_V2_LAST_MODIFIED_INDEX   44\n+#define NGX_HTTP_V2_LOCATION_INDEX        46\n+#define NGX_HTTP_V2_SERVER_INDEX          54\n+#define NGX_HTTP_V2_USER_AGENT_INDEX      58\n+#define NGX_HTTP_V2_VARY_INDEX            59\n \n #define NGX_HTTP_V2_NO_TRAILERS           (ngx_http_v2_out_frame_t *) -1\n \n \n+static const struct {\n+    u_char        *name;\n+    u_char const   len;\n+} push_header[] = {\n+    { (u_char*)\":authority\"      , 10 },\n+    { (u_char*)\"accept-encoding\" , 15 },\n+    { (u_char*)\"accept-language\" , 15 },\n+    { (u_char*)\"user-agent\"      , 10 }\n+};\n+\n+\n typedef struct {\n     ngx_str_t      name;\n     u_char         index;\n@@ -155,11 +198,9 @@\n #endif\n \n     static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);\n-    static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];\n \n     static size_t nginx_ver_build_len =\n                                   ngx_http_v2_literal_size(NGINX_VER_BUILD);\n-    static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];\n \n     stream = r->stream;\n \n@@ -435,7 +476,7 @@\n     }\n \n     tmp = ngx_palloc(r->pool, tmp_len);\n-    pos = ngx_pnalloc(r->pool, len);\n+    pos = ngx_pnalloc(r->pool, len + 15 + 1);\n \n     if (pos == NULL || tmp == NULL) {\n         return NGX_ERROR;\n@@ -443,11 +484,16 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n     }\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n@@ -458,67 +504,28 @@\n         *pos++ = status;\n \n     } else {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);\n-        *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;\n-        pos = ngx_sprintf(pos, \"%03ui\", r->headers_out.status);\n+        ngx_sprintf(pos + 8, \"%O3ui\", r->headers_out.status);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\":status\",\n+                                       sizeof(\":status\") - 1, pos + 8, 3, tmp);\n     }\n \n     if (r->headers_out.server == NULL) {\n-\n         if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER);\n-\n-        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER_BUILD);\n-\n-        } else {\n-            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: nginx\\\"\");\n-        }\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);\n-\n-        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            if (nginx_ver[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,\n-                                            sizeof(NGINX_VER) - 1, tmp);\n-                nginx_ver_len = p - nginx_ver;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER);\n \n         } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            if (nginx_ver_build[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver_build,\n-                                            (u_char *) NGINX_VER_BUILD,\n-                                            sizeof(NGINX_VER_BUILD) - 1, tmp);\n-                nginx_ver_build_len = p - nginx_ver_build;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER_BUILD);\n \n         } else {\n-            pos = ngx_cpymem(pos, nginx, sizeof(nginx));\n+            pos = ngx_http_v2_write_header_str(\"server\", \"nginx\");\n         }\n     }\n \n     if (r->headers_out.date == NULL) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"date: %V\\\"\",\n-                       &ngx_cached_http_time);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);\n-        pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,\n-                                      ngx_cached_http_time.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"date\", ngx_cached_http_time);\n     }\n \n     if (r->headers_out.content_type.len) {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);\n-\n         if (r->headers_out.content_type_len == r->headers_out.content_type.len\n             && r->headers_out.charset.len)\n         {\n@@ -544,64 +551,36 @@\n             r->headers_out.content_type.data = p - len;\n         }\n \n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-type: %V\\\"\",\n-                       &r->headers_out.content_type);\n-\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,\n-                                      r->headers_out.content_type.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"content-type\",\n+                                           r->headers_out.content_type);\n     }\n \n     if (r->headers_out.content_length == NULL\n         && r->headers_out.content_length_n >= 0)\n     {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-length: %O\\\"\",\n-                       r->headers_out.content_length_n);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);\n-\n-        p = pos;\n-        pos = ngx_sprintf(pos + 1, \"%O\", r->headers_out.content_length_n);\n-        *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);\n+        p = ngx_sprintf(pos + 15, \"%O\", r->headers_out.content_length_n);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"content-length\",\n+                                       sizeof(\"content-length\") - 1, pos + 15,\n+                                       p - (pos + 15), tmp);\n     }\n \n     if (r->headers_out.last_modified == NULL\n         && r->headers_out.last_modified_time != -1)\n     {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);\n-\n-        ngx_http_time(pos, r->headers_out.last_modified_time);\n+        ngx_http_time(pos + 14, r->headers_out.last_modified_time);\n         len = sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1;\n-\n-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"last-modified: %*s\\\"\",\n-                       len, pos);\n-\n-        /*\n-         * Date will always be encoded using huffman in the temporary buffer,\n-         * so it's safe here to use src and dst pointing to the same address.\n-         */\n-        pos = ngx_http_v2_write_value(pos, pos, len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"last-modified\",\n+                                       sizeof(\"last-modified\") - 1, pos + 14,\n+                                       len, tmp);\n     }\n \n     if (r->headers_out.location && r->headers_out.location->value.len) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"location: %V\\\"\",\n-                       &r->headers_out.location->value);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,\n-                                      r->headers_out.location->value.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"location\", r->headers_out.location->value);\n     }\n \n #if (NGX_HTTP_GZIP)\n     if (r->gzip_vary) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"vary: Accept-Encoding\\\"\");\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);\n-        pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));\n+        pos = ngx_http_v2_write_header_str(\"vary\", \"Accept-Encoding\");\n     }\n #endif\n \n@@ -624,23 +603,10 @@\n             continue;\n         }\n \n-#if (NGX_DEBUG)\n-        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n-            ngx_strlow(tmp, header[i].key.data, header[i].key.len);\n-\n-            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"%*s: %V\\\"\",\n-                           header[i].key.len, tmp, &header[i].value);\n-        }\n-#endif\n-\n-        *pos++ = 0;\n-\n-        pos = ngx_http_v2_write_name(pos, header[i].key.data,\n-                                     header[i].key.len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data,\n+                                       header[i].key.len, header[i].value.data,\n+                                       header[i].value.len, tmp);\n \n-        pos = ngx_http_v2_write_value(pos, header[i].value.data,\n-                                      header[i].value.len, tmp);\n     }\n \n     fin = r->header_only\n@@ -998,6 +964,7 @@\n \n     for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n         len += binary[i].len;\n+        len += push_header[i].len + 1;\n     }\n \n     pos = ngx_pnalloc(r->pool, len);\n@@ -1007,12 +974,17 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n-    }\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n+     }\n \n     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":method: GET\\\"\");\n@@ -1022,8 +994,7 @@\n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":path: %V\\\"\", path);\n \n-    *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n-    pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);\n+    pos = ngx_http_v2_write_header_pot(\":path\", path);\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":scheme: %V\\\"\", &r->schema);\n@@ -1048,11 +1019,15 @@\n             continue;\n         }\n \n+        value = &(*h)->value;\n+\n         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                        \"http2 push header: \\\"%V: %V\\\"\",\n                        &ph[i].name, &(*h)->value);\n \n-        pos = ngx_cpymem(pos, binary[i].data, binary[i].len);\n+        pos = ngx_http_v2_write_header(h2c, pos,\n+                  push_header[i].name, push_header[i].len, value->data, value->len,\n+                  tmp);\n     }\n \n     frame = ngx_http_v2_create_push_frame(r, start, pos);\ndiff --color -uNr a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h\n--- a/src/http/v2/ngx_http_v2.h\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.h\t2021-09-09 19:04:41.621383012 +0800\n@@ -52,6 +52,14 @@\n #define NGX_HTTP_V2_MAX_WINDOW           ((1U << 31) - 1)\n #define NGX_HTTP_V2_DEFAULT_WINDOW       65535\n \n+#define HPACK_ENC_HTABLE_SZ              128 /* better to keep a PoT < 64k */\n+#define HPACK_ENC_HTABLE_ENTRIES         ((HPACK_ENC_HTABLE_SZ * 100) / 128)\n+#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ     10  /* 10 is sufficient for most */\n+#define HPACK_ENC_MAX_ENTRY              512 /* longest header size to match */\n+\n+#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE     4096\n+#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE         16384 /* < 64k */\n+\n #define NGX_HTTP_V2_DEFAULT_WEIGHT       16\n \n \n@@ -115,6 +123,46 @@\n } ngx_http_v2_hpack_t;\n \n \n+#if (NGX_HTTP_V2_HPACK_ENC)\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen, vlen;\n+    uint16_t                         size;\n+    uint16_t                         next;\n+} ngx_http_v2_hpack_enc_entry_t;\n+\n+\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen;\n+} ngx_http_v2_hpack_name_entry_t;\n+\n+\n+typedef struct {\n+    size_t                           size;    /* size as defined in RFC 7541 */\n+    uint32_t                         top;     /* the last entry */\n+    uint32_t                         pos;\n+    uint16_t                         n_elems; /* number of elements */\n+    uint16_t                         base;    /* index of the oldest entry */\n+    uint16_t                         last;    /* index of the newest entry */\n+\n+    /* hash table for dynamic entries, instead using a generic hash table,\n+       which would be too slow to process a significant amount of headers,\n+       this table is not determenistic, and might ocasionally fail to insert\n+       a value, at the cost of slightly worse compression, but significantly\n+       faster performance */\n+    ngx_http_v2_hpack_enc_entry_t    htable[HPACK_ENC_HTABLE_SZ];\n+    ngx_http_v2_hpack_name_entry_t   heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ];\n+    u_char                           storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE +\n+                                             HPACK_ENC_MAX_ENTRY];\n+} ngx_http_v2_hpack_enc_t;\n+#endif\n+\n+\n struct ngx_http_v2_connection_s {\n     ngx_connection_t                *connection;\n     ngx_http_connection_t           *http_connection;\n@@ -136,6 +184,8 @@\n \n     size_t                           frame_size;\n \n+    size_t                           max_hpack_table_size;\n+\n     ngx_queue_t                      waiting;\n \n     ngx_http_v2_state_t              state;\n@@ -165,6 +215,11 @@\n     unsigned                         blocked:1;\n     unsigned                         goaway:1;\n     unsigned                         push_disabled:1;\n+    unsigned                         indicate_resize:1;\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+    ngx_http_v2_hpack_enc_t          hpack_enc;\n+#endif\n };\n \n \n@@ -208,6 +263,8 @@\n \n     ngx_array_t                     *cookies;\n \n+    size_t                           header_limit;\n+\n     ngx_pool_t                      *pool;\n \n     unsigned                         waiting:1;\n@@ -420,4 +477,35 @@\n     u_char *tmp, ngx_uint_t lower);\n \n \n+u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,\n+    u_char *tmp, ngx_uint_t lower);\n+\n+u_char *\n+ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value);\n+\n+#define ngx_http_v2_write_name(dst, src, len, tmp)                            \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 1)\n+#define ngx_http_v2_write_value(dst, src, len, tmp)                           \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 0)\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+    u_char *key, size_t key_len, u_char *value, size_t value_len,\n+    u_char *tmp);\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c);\n+\n+#define ngx_http_v2_write_header_str(key, value)                        \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    (u_char *) value, sizeof(value) - 1, tmp);\n+\n+#define ngx_http_v2_write_header_tbl(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val.data, val.len, tmp);\n+\n+#define ngx_http_v2_write_header_pot(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val->data, val->len, tmp);\n+\n #endif /* _NGX_HTTP_V2_H_INCLUDED_ */\ndiff --color -uNr a/src/http/v2/ngx_http_v2_table.c b/src/http/v2/ngx_http_v2_table.c\n--- a/src/http/v2/ngx_http_v2_table.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_table.c\t2021-09-09 19:04:41.624383090 +0800\n@@ -361,3 +361,434 @@\n \n     return NGX_OK;\n }\n+\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len);\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len);\n+\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c)\n+{\n+    ngx_http_v2_hpack_enc_entry_t  *table;\n+    uint64_t                        idx;\n+\n+    table = h2c->hpack_enc.htable;\n+\n+    while (h2c->hpack_enc.size > h2c->max_hpack_table_size) {\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+}\n+\n+\n+/* checks if a header is in the hpack table - if so returns the table entry,\n+   otherwise encodes and inserts into the table and returns 0,\n+   if failed to insert into table, returns -1 */\n+static ngx_int_t\n+ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c,\n+    size_t key_len, size_t val_len, uint8_t *key, uint8_t *val,\n+    ngx_int_t *header_idx)\n+{\n+    uint64_t  hash_val, key_hash, idx, lru;\n+    int       i;\n+    size_t    size = key_len + val_len + 32;\n+    uint8_t  *storage = h2c->hpack_enc.storage;\n+\n+    ngx_http_v2_hpack_enc_entry_t   *table;\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+\n+    *header_idx = NGX_ERROR;\n+    /* step 1: compute the hash value of header */\n+    if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) {\n+        return NGX_ERROR;\n+    }\n+\n+    key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234);\n+    hash_val = ngx_murmur_hash2_64(val, val_len, key_hash);\n+\n+    if (hash_val == 0) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* step 2: check if full header in the table */\n+    idx = hash_val;\n+    i = -1;\n+    while (idx) {\n+         /* at most 8 locations are checked, but most will be done in 1 or 2 */\n+        table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ];\n+        if (table->hash_val == hash_val\n+            && table->klen == key_len\n+            && table->vlen == val_len\n+            && ngx_memcmp(key, storage + table->pos, key_len) == 0\n+            && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0)\n+        {\n+            return (h2c->hpack_enc.top - table->index) + 61;\n+        }\n+\n+        if (table->hash_val == 0 && i == -1) {\n+            i = idx % HPACK_ENC_HTABLE_SZ;\n+            break;\n+        }\n+\n+        idx >>= 8;\n+    }\n+\n+    /* step 3: check if key is in one of the tables */\n+    *header_idx = hpack_get_static_index(h2c, key, key_len);\n+\n+    if (i == -1) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (*header_idx == NGX_ERROR) {\n+        *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len);\n+    }\n+\n+    /* step 4: store the new entry */\n+    table =  h2c->hpack_enc.htable;\n+\n+    if (h2c->hpack_enc.top == 0xffffffff) {\n+        /* just to be on the safe side, avoid overflow */\n+        ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t));\n+    }\n+\n+    while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size)\n+           || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) {\n+        /* make space for the new entry first */\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+\n+    table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val,\n+                                               .index = h2c->hpack_enc.top,\n+                                               .pos = h2c->hpack_enc.pos,\n+                                               .klen = key_len,\n+                                               .vlen = val_len,\n+                                               .size = size,\n+                                               .next = 0};\n+\n+    table[h2c->hpack_enc.last].next = i;\n+    if (h2c->hpack_enc.n_elems == 0) {\n+        h2c->hpack_enc.base = i;\n+    }\n+\n+    h2c->hpack_enc.last = i;\n+    h2c->hpack_enc.top++;\n+    h2c->hpack_enc.size += size;\n+    h2c->hpack_enc.n_elems++;\n+\n+    /* update header name lookup */\n+    if (*header_idx == NGX_ERROR ) {\n+        lru = h2c->hpack_enc.top;\n+\n+        for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+\n+            name = &h2c->hpack_enc.heads[i];\n+\n+            if ( name->hash_val == 0 || (name->hash_val == key_hash\n+                && ngx_memcmp(storage + name->pos, key, key_len) == 0) )\n+            {\n+                name->hash_val = key_hash;\n+                name->pos = h2c->hpack_enc.pos;\n+                name->index = h2c->hpack_enc.top - 1;\n+                break;\n+            }\n+\n+            if (lru > name->index) {\n+                lru = name->index;\n+                idx = i;\n+            }\n+        }\n+\n+        if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) {\n+            name = &h2c->hpack_enc.heads[idx];\n+            name->hash_val = hash_val;\n+            name->pos = h2c->hpack_enc.pos;\n+            name->index = h2c->hpack_enc.top - 1;\n+        }\n+    }\n+\n+    ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len);\n+    ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len);\n+\n+    h2c->hpack_enc.pos += size;\n+    if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+        h2c->hpack_enc.pos = 0;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_int_t idx, header_idx;\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    /* attempt to find the value in the dynamic table */\n+    idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value,\n+                                           &header_idx);\n+\n+    if (idx > 0) {\n+        /* positive index indicates success */\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                       \"http2 hpack encode: Indexed Header Field: %ud\", idx);\n+\n+        *pos = 128;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx);\n+\n+    } else {\n+\n+        if (header_idx == NGX_ERROR) { /* if key is not present */\n+\n+            if (idx == NGX_ERROR) {    /* if header was not added */\n+                *pos++ = 0;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — New Name\");\n+            } else {                   /* if header was added */\n+                *pos++ = 64;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — New Name\");\n+            }\n+\n+            pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+\n+        } else {                       /* if key is present */\n+\n+            if (idx == NGX_ERROR) {\n+                *pos = 0;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — Indexed Name: %ud\", header_idx);\n+            } else {\n+                *pos = 64;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — Indexed Name: %ud\", header_idx);\n+            }\n+        }\n+\n+        pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+    }\n+\n+    return pos;\n+}\n+\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len)\n+{\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+    int                              i;\n+\n+    for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+        name = &h2c->hpack_enc.heads[i];\n+\n+        if (name->hash_val == key_hash\n+            && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0)\n+        {\n+            if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) {\n+                return (h2c->hpack_enc.top - name->index) + 61;\n+            }\n+            break;\n+        }\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+\n+/* decide if a given header is present in the static dictionary, this could be\n+   done in several ways, but it seems the fastest one is \"exhaustive\" search */\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len)\n+{\n+    /* the static dictionary of response only headers,\n+       although response headers can be put by origin,\n+       that would be rare */\n+    static const struct {\n+        u_char         len;\n+        const u_char   val[28];\n+        u_char         idx;\n+    } server_headers[] = {\n+        { 3, \"age\",                         21},//0\n+        { 3, \"via\",                         60},\n+        { 4, \"date\",                        33},//2\n+        { 4, \"etag\",                        34},\n+        { 4, \"link\",                        45},\n+        { 4, \"vary\",                        59},\n+        { 5, \"allow\",                       22},//6\n+        { 6, \"server\",                      54},//7\n+        { 7, \"expires\",                     36},//8\n+        { 7, \"refresh\",                     52},\n+        { 8, \"location\",                    46},//10\n+        {10, \"set-cookie\",                  55},//11\n+        {11, \"retry-after\",                 53},//12\n+        {12, \"content-type\",                31},//13\n+        {13, \"content-range\",               30},//14\n+        {13, \"accept-ranges\",               18},\n+        {13, \"cache-control\",               24},\n+        {13, \"last-modified\",               44},\n+        {14, \"content-length\",              28},//18\n+        {16, \"content-encoding\",            26},//19\n+        {16, \"content-language\",            27},\n+        {16, \"content-location\",            29},\n+        {16, \"www-authenticate\",            61},\n+        {17, \"transfer-encoding\",           57},//23\n+        {18, \"proxy-authenticate\",          48},//24\n+        {19, \"content-disposition\",         25},//25\n+        {25, \"strict-transport-security\",   56},//26\n+        {27, \"access-control-allow-origin\", 20},//27\n+        {99, \"\",                            99},\n+    }, *header;\n+\n+    /* for a given length, where to start the search\n+       since minimal length is 3, the table has a -3\n+       offset */\n+    static const int8_t start_at[] = {\n+        [3-3]  = 0,\n+        [4-3]  = 2,\n+        [5-3]  = 6,\n+        [6-3]  = 7,\n+        [7-3]  = 8,\n+        [8-3]  = 10,\n+        [9-3]  = -1,\n+        [10-3] = 11,\n+        [11-3] = 12,\n+        [12-3] = 13,\n+        [13-3] = 14,\n+        [14-3] = 18,\n+        [15-3] = -1,\n+        [16-3] = 19,\n+        [17-3] = 23,\n+        [18-3] = 24,\n+        [19-3] = 25,\n+        [20-3] = -1,\n+        [21-3] = -1,\n+        [22-3] = -1,\n+        [23-3] = -1,\n+        [24-3] = -1,\n+        [25-3] = 26,\n+        [26-3] = -1,\n+        [27-3] = 27,\n+    };\n+\n+    uint64_t pref;\n+    size_t   save_len = len, i;\n+    int8_t   start;\n+\n+    /* early exit for out of bounds lengths */\n+    if (len < 3 || len > 27) {\n+        return NGX_ERROR;\n+    }\n+\n+    start = start_at[len - 3];\n+    if (start == -1) {\n+        /* exit for non existent lengths */\n+        return NGX_ERROR;\n+    }\n+\n+    header = &server_headers[start_at[len - 3]];\n+\n+    /* load first 8 bytes of key, for fast comparison */\n+    if (len < 8) {\n+        pref = 0;\n+        if (len >= 4) {\n+            pref = *(uint32_t *)(val + len - 4) | 0x20202020;\n+            len -= 4;\n+        }\n+        while (len > 0) { /* 3 iterations at most */\n+            pref = (pref << 8) ^ (val[len - 1] | 0x20);\n+            len--;\n+        }\n+    } else {\n+        pref = *(uint64_t *)val | 0x2020202020202020;\n+        len -= 8;\n+    }\n+\n+    /* iterate over headers with the right length */\n+    while (header->len == save_len) {\n+        /* quickly compare the first 8 bytes, most tests will end here */\n+        if (pref != *(uint64_t *) header->val) {\n+            header++;\n+            continue;\n+        }\n+\n+        if (len == 0) {\n+            /* len == 0, indicates prefix held the entire key */\n+            return header->idx;\n+        }\n+        /* for longer keys compare the rest */\n+        i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */\n+\n+        while (i + 8 <= save_len) { /* 3 iterations at most */\n+            if ( *(uint64_t *)&header->val[i]\n+                 != (*(uint64_t *) &val[i]| 0x2020202020202020) )\n+            {\n+                header++;\n+                i = 0;\n+                break;\n+            }\n+            i += 8;\n+        }\n+\n+        if (i == 0) {\n+            continue;\n+        }\n+\n+        /* found the corresponding entry in the static dictionary */\n+        return header->idx;\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+#else\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    *pos++ = 64;\n+    pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+    pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+\n+    return pos;\n+}\n+\n+#endif\ndiff --color -uNr a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c\n--- a/src/http/v3/ngx_http_v3.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3.c\t2021-09-09 19:04:29.397062871 +0800\n@@ -0,0 +1,2231 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+\n+typedef struct {\n+    ngx_str_t           name;\n+    ngx_uint_t          offset;\n+    ngx_uint_t          hash;\n+    ngx_http_header_t  *hh;\n+} ngx_http_v3_parse_header_t;\n+\n+\n+/* errors */\n+#define NGX_HTTP_V3_NO_ERROR                     0x0100\n+#define NGX_HTTP_V3_PROTOCOL_ERROR               0x0101\n+#define NGX_HTTP_V3_INTERNAL_ERROR               0x0102\n+\n+\n+static void ngx_http_v3_handler(ngx_connection_t *c);\n+\n+static void ngx_http_v3_idle_handler(ngx_connection_t *c);\n+\n+static void ngx_http_v3_handle_connection(ngx_http_v3_connection_t *h3c);\n+\n+static ngx_http_v3_stream_t *ngx_http_v3_stream_lookup(\n+    ngx_http_v3_connection_t *h3c, ngx_uint_t stream_id);\n+static ngx_http_v3_stream_t *ngx_http_v3_create_stream(\n+    ngx_http_v3_connection_t *h3c);\n+static void ngx_http_v3_close_stream_handler(ngx_event_t *ev);\n+\n+static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r,\n+    ngx_http_v3_header_t *header);\n+static ngx_int_t ngx_http_v3_pseudo_header(ngx_http_request_t *r,\n+    ngx_http_v3_header_t *header);\n+static ngx_int_t ngx_http_v3_parse_path(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_method(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_scheme(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_authority(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r,\n+    ngx_http_v3_parse_header_t *header, ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_cookie(ngx_http_request_t *r,\n+    ngx_http_v3_header_t *header);\n+static ngx_int_t ngx_http_v3_construct_cookie_header(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_v3_construct_request_line(ngx_http_request_t *r);\n+\n+static void ngx_http_v3_run_request(ngx_http_request_t *r);\n+\n+static ssize_t ngx_http_v3_recv_body(ngx_connection_t *c, u_char *buf,\n+    size_t size);\n+static ngx_chain_t *ngx_http_v3_send_chain(ngx_connection_t *fc,\n+    ngx_chain_t *in, off_t limit);\n+\n+static void ngx_http_v3_finalize_connection(ngx_http_v3_connection_t *h3c,\n+    ngx_uint_t status);\n+\n+static void ngx_http_v3_pool_cleanup(void *data);\n+\n+\n+static ngx_http_v3_parse_header_t  ngx_http_v3_parse_headers[] = {\n+    { ngx_string(\"host\"),\n+      offsetof(ngx_http_headers_in_t, host), 0, NULL },\n+\n+    { ngx_string(\"accept-encoding\"),\n+      offsetof(ngx_http_headers_in_t, accept_encoding), 0, NULL },\n+\n+    { ngx_string(\"accept-language\"),\n+      offsetof(ngx_http_headers_in_t, accept_language), 0, NULL },\n+\n+    { ngx_string(\"user-agent\"),\n+      offsetof(ngx_http_headers_in_t, user_agent), 0, NULL },\n+\n+    { ngx_null_string, 0, 0, NULL }\n+};\n+\n+\n+void\n+ngx_http_v3_init(ngx_event_t *rev)\n+{\n+    ngx_connection_t          *c;\n+    ngx_pool_cleanup_t        *cln;\n+    ngx_http_connection_t     *hc;\n+    ngx_http_v3_srv_conf_t    *h3scf;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    c = rev->data;\n+    hc = c->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"init http3 connection\");\n+\n+    c->log->action = \"processing HTTP/3 connection\";\n+\n+    h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t));\n+    if (h3c == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);\n+\n+    h3c->h3 = quiche_h3_conn_new_with_transport(c->quic->conn, h3scf->http3);\n+    if (h3c->h3 == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    h3c->http_connection = hc;\n+\n+    h3c->connection = c;\n+\n+    h3c->pool = c->pool;\n+\n+    c->data = h3c;\n+\n+    c->quic->handler = ngx_http_v3_handler;\n+\n+    cln = ngx_pool_cleanup_add(c->pool, 0);\n+    if (cln == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    cln->handler = ngx_http_v3_pool_cleanup;\n+    cln->data = h3c;\n+\n+    ngx_rbtree_init(&h3c->streams, &h3c->streams_sentinel,\n+                    ngx_rbtree_insert_value);\n+}\n+\n+\n+static int\n+ngx_http_v3_for_each_header(uint8_t *name, size_t name_len,\n+    uint8_t *value, size_t value_len, void *argp)\n+{\n+    ngx_int_t                   rc;\n+    ngx_table_elt_t            *h;\n+    ngx_http_header_t          *hh;\n+    ngx_http_request_t         *r;\n+    ngx_http_v3_header_t        header;\n+    ngx_http_core_srv_conf_t   *cscf;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    static ngx_str_t cookie = ngx_string(\"cookie\");\n+\n+    r = argp;\n+\n+    /* Duplicate the header name because we don't own it. */\n+    header.name.data = ngx_pnalloc(r->pool, name_len);\n+    if (header.name.data == NULL) {\n+        return NGX_ERROR;\n+    }\n+    header.name.len = name_len;\n+\n+    ngx_memcpy(header.name.data, name, name_len);\n+\n+    /* Duplicate the header value because we don't own it. Some of the\n+     * functions that process headers require a NULL-terminated string,\n+     * so allocate enough memory for that. */\n+    header.value.data = ngx_pcalloc(r->pool, value_len + 1);\n+    if (header.value.data == NULL) {\n+        return NGX_ERROR;\n+    }\n+    header.value.len = value_len;\n+\n+    ngx_memcpy(header.value.data, value, value_len);\n+\n+    if (ngx_http_v3_validate_header(r, &header) != NGX_OK) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* Check for pseudo-header. */\n+    if (header.name.data[0] == ':') {\n+        rc = ngx_http_v3_pseudo_header(r, &header);\n+\n+        if (rc == NGX_OK) {\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                           \"http3 header: \\\":%V: %V\\\"\",\n+                           &header.name, &header.value);\n+\n+            return NGX_OK;\n+        }\n+\n+        return NGX_ERROR;\n+    }\n+\n+    if (r->invalid_header) {\n+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+        if (cscf->ignore_invalid_headers) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid header: \\\"%V\\\"\", &header.name);\n+\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    /* Handle Cookie header separately. Not sure why, but the HTTP/2 code does\n+     * the same. */\n+    if (header.name.len == cookie.len\n+        && ngx_memcmp(header.name.data, cookie.data, cookie.len) == 0)\n+    {\n+        if (ngx_http_v3_cookie(r, &header) != NGX_OK) {\n+            return NGX_ERROR;\n+        }\n+\n+    } else {\n+        h = ngx_list_push(&r->headers_in.headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->key.len = header.name.len;\n+        h->key.data = header.name.data;\n+\n+        /*\n+         * TODO Optimization: precalculate hash\n+         * and handler for indexed headers.\n+         */\n+        h->hash = ngx_hash_key(h->key.data, h->key.len);\n+\n+        h->value.len = header.value.len;\n+        h->value.data = header.value.data;\n+\n+        h->lowcase_key = h->key.data;\n+\n+        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+        hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n+                           h->lowcase_key, h->key.len);\n+\n+        if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 header: \\\"%V: %V\\\"\",\n+                   &header.name, &header.value);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_process_headers(ngx_connection_t *c, quiche_h3_event *ev,\n+    int64_t stream_id)\n+{\n+    int                        rc;\n+    ngx_http_v3_stream_t      *stream;\n+    ngx_http_v3_srv_conf_t    *h3scf;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 process headers\");\n+\n+    h3c = c->data;\n+\n+    h3scf = ngx_http_get_module_srv_conf(h3c->http_connection->conf_ctx,\n+                                         ngx_http_v3_module);\n+\n+    if (h3c->connection->requests >= h3scf->max_requests) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_NO_ERROR);\n+        return;\n+    }\n+\n+    /* Create a new stream to handle the incoming request. */\n+    stream = ngx_http_v3_create_stream(h3c);\n+    if (stream == NULL) {\n+        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to create HTTP/3 stream\");\n+\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    stream->id = stream_id;\n+\n+    stream->node.key = stream_id;\n+\n+    ngx_rbtree_insert(&h3c->streams, &stream->node);\n+\n+    /* Populate ngx_http_request_t from raw HTTP/3 headers. */\n+    rc = quiche_h3_event_for_each_header(ev,\n+        ngx_http_v3_for_each_header, stream->request);\n+\n+    if (rc != NGX_OK) {\n+        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n+                      \"received invalid HTTP/3 headers\");\n+\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    stream->in_closed = !quiche_h3_event_headers_has_body(ev);\n+\n+    ngx_http_v3_run_request(stream->request);\n+}\n+\n+\n+static void\n+ngx_http_v3_process_blocked_streams(ngx_http_v3_connection_t *h3c)\n+{\n+    ngx_event_t               *wev;\n+    quiche_stream_iter        *writable;\n+    ngx_http_v3_stream_t      *stream;\n+    uint64_t                   stream_id;\n+\n+    writable = quiche_conn_writable(h3c->connection->quic->conn);\n+\n+    while (quiche_stream_iter_next(writable, &stream_id)) {\n+        stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+        if (stream == NULL) {\n+            continue;\n+        }\n+\n+        if (!stream->blocked) {\n+            continue;\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                       \"http3 stream unblocked %ui\", stream->id);\n+\n+        stream->blocked = 0;\n+\n+        wev = stream->request->connection->write;\n+\n+        wev->active = 0;\n+        wev->ready = 1;\n+\n+        if (!stream->headers_sent) {\n+            ngx_http_v3_send_response(stream->request);\n+        }\n+\n+        if (!wev->delayed) {\n+            wev->handler(wev);\n+        }\n+    }\n+\n+    quiche_stream_iter_free(writable);\n+}\n+\n+\n+static void\n+ngx_http_v3_handler(ngx_connection_t *c)\n+{\n+    ngx_chain_t                out;\n+    ngx_connection_t          *fc;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_connection_t  *h3c;\n+    ngx_http_v3_stream_t      *stream;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 handler\");\n+\n+    h3c = c->data;\n+\n+    if (c->read->timedout) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_PROTOCOL_ERROR);\n+        return;\n+    }\n+\n+    if (c->error) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    ngx_http_v3_process_blocked_streams(h3c);\n+\n+    while (!c->error) {\n+        quiche_h3_event  *ev;\n+\n+        int64_t stream_id = quiche_h3_conn_poll(h3c->h3, c->quic->conn, &ev);\n+        if (stream_id == QUICHE_H3_ERR_DONE) {\n+            break;\n+        }\n+\n+        if (stream_id < 0) {\n+            ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_PROTOCOL_ERROR);\n+            return;\n+        }\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                       \"http3 event stream:%ui ev:%ui\", stream_id,\n+                       quiche_h3_event_type(ev));\n+\n+        switch (quiche_h3_event_type(ev)) {\n+            case QUICHE_H3_EVENT_HEADERS: {\n+                ngx_http_v3_process_headers(c, ev, stream_id);\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_DATA: {\n+                /* Lookup stream. If there isn't one, it means it has already\n+                 * been closed, so ignore the event. */\n+                stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+                if (stream != NULL && !stream->in_closed) {\n+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                                   \"http3 data\");\n+\n+                    ngx_post_event(stream->request->connection->read,\n+                                   &ngx_posted_events);\n+                }\n+\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_FINISHED: {\n+                /* Lookup stream. If there isn't one, it means it has already\n+                 * been closed, so ignore the event. */\n+                stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+                if (stream != NULL && !stream->in_closed) {\n+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                                   \"http3 finished\");\n+\n+                    /* Flush request body that was buffered. */\n+                    if (stream->request->request_body) {\n+                        out.buf = stream->request->request_body->buf;\n+                        out.next = NULL;\n+\n+                        ngx_http_v3_request_body_filter(stream->request, &out);\n+\n+                        ngx_post_event(stream->request->connection->read,\n+                                       &ngx_posted_events);\n+                    }\n+\n+                    stream->in_closed = 1;\n+                }\n+\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_RESET: {\n+                /* Lookup stream. If there isn't one, it means it has already\n+                 * been closed, so ignore the event. */\n+                stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+                if (stream != NULL && !stream->in_closed) {\n+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                                   \"http3 reset\");\n+\n+                    r = stream->request;\n+                    fc = r->connection;\n+\n+                    fc->error = 1;\n+\n+                    ngx_post_event(stream->request->connection->read,\n+                                   &ngx_posted_events);\n+\n+                    stream->in_closed = 1;\n+                }\n+\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_DATAGRAM:\n+                break;\n+\n+            case QUICHE_H3_EVENT_GOAWAY:\n+                break;\n+        }\n+\n+        quiche_h3_event_free(ev);\n+    }\n+\n+    ngx_http_v3_handle_connection(h3c);\n+}\n+\n+\n+static void\n+ngx_http_v3_idle_handler(ngx_connection_t *c)\n+{\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 idle handler\");\n+\n+    h3c = c->data;\n+\n+    if (c->read->timedout) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_NO_ERROR);\n+        return;\n+    }\n+\n+    if (c->error) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    if (!quiche_conn_is_readable(c->quic->conn)) {\n+        return;\n+    }\n+\n+    if (c->read->timer_set) {\n+        ngx_del_timer(c->read);\n+    }\n+\n+    c->quic->handler = ngx_http_v3_handler;\n+\n+    ngx_http_v3_handler(c);\n+}\n+\n+\n+static void\n+ngx_http_v3_handle_connection(ngx_http_v3_connection_t *h3c)\n+{\n+    ngx_connection_t        *c;\n+    ngx_http_v3_srv_conf_t  *h3scf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 handle connection\");\n+\n+    c = h3c->connection;\n+\n+    if (h3c->processing || c->error) {\n+        return;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 connection is idle\");\n+\n+    h3scf = ngx_http_get_module_srv_conf(h3c->http_connection->conf_ctx,\n+                                         ngx_http_v3_module);\n+\n+    c->quic->handler = ngx_http_v3_idle_handler;\n+\n+    ngx_add_timer(c->read, h3scf->idle_timeout);\n+}\n+\n+\n+static ngx_http_v3_stream_t *\n+ngx_http_v3_create_stream(ngx_http_v3_connection_t *h3c)\n+{\n+    ngx_log_t                 *log;\n+    ngx_event_t               *rev, *wev;\n+    ngx_connection_t          *fc;\n+    ngx_http_log_ctx_t        *ctx;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_stream_t      *stream;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 create stream\");\n+\n+    fc = h3c->free_fake_connections;\n+\n+    if (fc) {\n+        h3c->free_fake_connections = fc->data;\n+\n+        rev = fc->read;\n+        wev = fc->write;\n+        log = fc->log;\n+        ctx = log->data;\n+\n+    } else {\n+        fc = ngx_palloc(h3c->pool, sizeof(ngx_connection_t));\n+        if (fc == NULL) {\n+            return NULL;\n+        }\n+\n+        rev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));\n+        if (rev == NULL) {\n+            return NULL;\n+        }\n+\n+        wev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));\n+        if (wev == NULL) {\n+            return NULL;\n+        }\n+\n+        log = ngx_palloc(h3c->pool, sizeof(ngx_log_t));\n+        if (log == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx = ngx_palloc(h3c->pool, sizeof(ngx_http_log_ctx_t));\n+        if (ctx == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx->connection = fc;\n+        ctx->request = NULL;\n+        ctx->current_request = NULL;\n+    }\n+\n+    ngx_memcpy(log, h3c->connection->log, sizeof(ngx_log_t));\n+\n+    log->data = ctx;\n+\n+    ngx_memzero(rev, sizeof(ngx_event_t));\n+\n+    rev->data = fc;\n+    rev->ready = 1;\n+    rev->handler = ngx_http_v3_close_stream_handler;\n+    rev->log = log;\n+\n+    ngx_memcpy(wev, rev, sizeof(ngx_event_t));\n+\n+    wev->write = 1;\n+\n+    ngx_memcpy(fc, h3c->connection, sizeof(ngx_connection_t));\n+\n+    fc->data = h3c->http_connection;\n+    fc->quic = h3c->connection->quic;\n+    fc->read = rev;\n+    fc->write = wev;\n+    fc->sent = 0;\n+    fc->buffer = NULL;\n+    fc->log = log;\n+    fc->buffered = 0;\n+    fc->sndlowat = 1;\n+    fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n+\n+    fc->recv = ngx_http_v3_recv_body;\n+\n+    fc->send_chain = ngx_http_v3_send_chain;\n+    fc->need_last_buf = 1;\n+\n+    r = ngx_http_create_request(fc);\n+    if (r == NULL) {\n+        return NULL;\n+    }\n+\n+    ngx_str_set(&r->http_protocol, \"HTTP/3\");\n+\n+    r->http_version = NGX_HTTP_VERSION_3;\n+    r->valid_location = 1;\n+\n+    fc->data = r;\n+    h3c->connection->requests++;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    r->header_in = ngx_create_temp_buf(r->pool,\n+                                       cscf->client_header_buffer_size);\n+    if (r->header_in == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    if (ngx_list_init(&r->headers_in.headers, r->pool, 20,\n+                      sizeof(ngx_table_elt_t))\n+        != NGX_OK)\n+    {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n+\n+    stream = ngx_pcalloc(h3c->pool, sizeof(ngx_http_v3_stream_t));\n+    if (stream == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->qstream = stream;\n+\n+    stream->request = r;\n+    stream->connection = h3c;\n+\n+    h3c->processing++;\n+\n+    return stream;\n+}\n+\n+\n+static ngx_http_v3_stream_t *\n+ngx_http_v3_stream_lookup(ngx_http_v3_connection_t *h3c, ngx_uint_t stream_id)\n+{\n+    ngx_rbtree_node_t  *node, *sentinel;\n+\n+    node = h3c->streams.root;\n+    sentinel = h3c->streams.sentinel;\n+\n+    while (node != sentinel) {\n+\n+        if (stream_id < node->key) {\n+            node = node->left;\n+            continue;\n+        }\n+\n+        if (stream_id > node->key) {\n+            node = node->right;\n+            continue;\n+        }\n+\n+        /* stream_id == node->key */\n+\n+        return (ngx_http_v3_stream_t *) node;\n+    }\n+\n+    /* not found */\n+\n+    return NULL;\n+}\n+\n+\n+/* The following functions are copied from the HTTP/2 module, and adapted to\n+ * work independently. In theory we could refactor the HTTP/2 module to expose\n+ * these functions, but that would be fairly invasive and likely cause more\n+ * merge conflicts in the future. */\n+\n+\n+static ngx_int_t\n+ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n+{\n+    u_char                     ch;\n+    ngx_uint_t                 i;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    if (header->name.len == 0) {\n+        return NGX_ERROR;\n+    }\n+\n+    r->invalid_header = 0;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    for (i = (header->name.data[0] == ':'); i != header->name.len; i++) {\n+        ch = header->name.data[i];\n+\n+        if ((ch >= 'a' && ch <= 'z')\n+            || (ch == '-')\n+            || (ch >= '0' && ch <= '9')\n+            || (ch == '_' && cscf->underscores_in_headers))\n+        {\n+            continue;\n+        }\n+\n+        if (ch == '\\0' || ch == LF || ch == CR || ch == ':'\n+            || (ch >= 'A' && ch <= 'Z'))\n+        {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid header name: \\\"%V\\\"\",\n+                          &header->name);\n+\n+            return NGX_ERROR;\n+        }\n+\n+        r->invalid_header = 1;\n+    }\n+\n+    for (i = 0; i != header->value.len; i++) {\n+        ch = header->value.data[i];\n+\n+        if (ch == '\\0' || ch == LF || ch == CR) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent header \\\"%V\\\" with \"\n+                          \"invalid value: \\\"%V\\\"\",\n+                          &header->name, &header->value);\n+\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_pseudo_header(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n+{\n+    header->name.len--;\n+    header->name.data++;\n+\n+    switch (header->name.len) {\n+    case 4:\n+        if (ngx_memcmp(header->name.data, \"path\", sizeof(\"path\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_path(r, &header->value);\n+        }\n+\n+        break;\n+\n+    case 6:\n+        if (ngx_memcmp(header->name.data, \"method\", sizeof(\"method\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_method(r, &header->value);\n+        }\n+\n+        if (ngx_memcmp(header->name.data, \"scheme\", sizeof(\"scheme\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_scheme(r, &header->value);\n+        }\n+\n+        break;\n+\n+    case 9:\n+        if (ngx_memcmp(header->name.data, \"authority\", sizeof(\"authority\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_authority(r, &header->value);\n+        }\n+\n+        break;\n+    }\n+\n+    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                  \"client sent unknown pseudo-header \\\":%V\\\"\",\n+                  &header->name);\n+\n+    return NGX_DECLINED;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_path(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    if (r->unparsed_uri.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :path header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (value->len == 0) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent empty :path header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    r->uri_start = value->data;\n+    r->uri_end = value->data + value->len;\n+\n+    if (ngx_http_parse_uri(r) != NGX_OK) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent invalid :path header: \\\"%V\\\"\", value);\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (ngx_http_process_request_uri(r) != NGX_OK) {\n+        /*\n+         * request has been finalized already\n+         * in ngx_http_process_request_uri()\n+         */\n+        return NGX_ABORT;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_method(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    size_t         k, len;\n+    ngx_uint_t     n;\n+    const u_char  *p, *m;\n+\n+    /*\n+     * This array takes less than 256 sequential bytes,\n+     * and if typical CPU cache line size is 64 bytes,\n+     * it is prefetched for 4 load operations.\n+     */\n+    static const struct {\n+        u_char            len;\n+        const u_char      method[11];\n+        uint32_t          value;\n+    } tests[] = {\n+        { 3, \"GET\",       NGX_HTTP_GET },\n+        { 4, \"POST\",      NGX_HTTP_POST },\n+        { 4, \"HEAD\",      NGX_HTTP_HEAD },\n+        { 7, \"OPTIONS\",   NGX_HTTP_OPTIONS },\n+        { 8, \"PROPFIND\",  NGX_HTTP_PROPFIND },\n+        { 3, \"PUT\",       NGX_HTTP_PUT },\n+        { 5, \"MKCOL\",     NGX_HTTP_MKCOL },\n+        { 6, \"DELETE\",    NGX_HTTP_DELETE },\n+        { 4, \"COPY\",      NGX_HTTP_COPY },\n+        { 4, \"MOVE\",      NGX_HTTP_MOVE },\n+        { 9, \"PROPPATCH\", NGX_HTTP_PROPPATCH },\n+        { 4, \"LOCK\",      NGX_HTTP_LOCK },\n+        { 6, \"UNLOCK\",    NGX_HTTP_UNLOCK },\n+        { 5, \"PATCH\",     NGX_HTTP_PATCH },\n+        { 5, \"TRACE\",     NGX_HTTP_TRACE }\n+    }, *test;\n+\n+    if (r->method_name.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :method header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (value->len == 0) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent empty :method header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    r->method_name.len = value->len;\n+    r->method_name.data = value->data;\n+\n+    len = r->method_name.len;\n+    n = sizeof(tests) / sizeof(tests[0]);\n+    test = tests;\n+\n+    do {\n+        if (len == test->len) {\n+            p = r->method_name.data;\n+            m = test->method;\n+            k = len;\n+\n+            do {\n+                if (*p++ != *m++) {\n+                    goto next;\n+                }\n+            } while (--k);\n+\n+            r->method = test->value;\n+            return NGX_OK;\n+        }\n+\n+    next:\n+        test++;\n+\n+    } while (--n);\n+\n+    p = r->method_name.data;\n+\n+    do {\n+        if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid method: \\\"%V\\\"\",\n+                          &r->method_name);\n+\n+            return NGX_DECLINED;\n+        }\n+\n+        p++;\n+\n+    } while (--len);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    u_char      c, ch;\n+    ngx_uint_t  i;\n+\n+    if (r->schema.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :scheme header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (value->len == 0) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent empty :scheme header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    for (i = 0; i < value->len; i++) {\n+        ch = value->data[i];\n+\n+        c = (u_char) (ch | 0x20);\n+        if (c >= 'a' && c <= 'z') {\n+            continue;\n+        }\n+\n+        if (((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')\n+            && i > 0)\n+        {\n+            continue;\n+        }\n+\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent invalid :scheme header: \\\"%V\\\"\", value);\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    r->schema = *value;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_authority(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    return ngx_http_v3_parse_header(r, &ngx_http_v3_parse_headers[0], value);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_header(ngx_http_request_t *r,\n+    ngx_http_v3_parse_header_t *header, ngx_str_t *value)\n+{\n+    ngx_table_elt_t            *h;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    h->key.len = header->name.len;\n+    h->key.data = header->name.data;\n+    h->lowcase_key = header->name.data;\n+\n+    if (header->hh == NULL) {\n+        header->hash = ngx_hash_key(header->name.data, header->name.len);\n+\n+        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+        header->hh = ngx_hash_find(&cmcf->headers_in_hash, header->hash,\n+                                   h->lowcase_key, h->key.len);\n+        if (header->hh == NULL) {\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    h->hash = header->hash;\n+\n+    h->value.len = value->len;\n+    h->value.data = value->data;\n+\n+    if (header->hh->handler(r, h, header->hh->offset) != NGX_OK) {\n+        /* header handler has already finalized request */\n+        return NGX_ABORT;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_construct_request_line(ngx_http_request_t *r)\n+{\n+    u_char  *p;\n+\n+    static const u_char ending[] = \" HTTP/3\";\n+\n+    if (r->method_name.len == 0\n+        || r->schema.len == 0\n+        || r->unparsed_uri.len == 0)\n+    {\n+        if (r->method_name.len == 0) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent no :method header\");\n+\n+        } else if (r->schema.len == 0) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent no :scheme header\");\n+\n+        } else {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent no :path header\");\n+        }\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.len = r->method_name.len + 1\n+                          + r->unparsed_uri.len\n+                          + sizeof(ending) - 1;\n+\n+    p = ngx_pnalloc(r->pool, r->request_line.len + 1);\n+    if (p == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.data = p;\n+\n+    p = ngx_cpymem(p, r->method_name.data, r->method_name.len);\n+\n+    *p++ = ' ';\n+\n+    p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);\n+\n+    ngx_memcpy(p, ending, sizeof(ending));\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 request line: \\\"%V\\\"\", &r->request_line);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_cookie(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n+{\n+    ngx_str_t    *val;\n+    ngx_array_t  *cookies;\n+\n+    cookies = r->qstream->cookies;\n+\n+    if (cookies == NULL) {\n+        cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t));\n+        if (cookies == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        r->qstream->cookies = cookies;\n+    }\n+\n+    val = ngx_array_push(cookies);\n+    if (val == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    val->len = header->value.len;\n+    val->data = header->value.data;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_construct_cookie_header(ngx_http_request_t *r)\n+{\n+    u_char                     *buf, *p, *end;\n+    size_t                      len;\n+    ngx_str_t                  *vals;\n+    ngx_uint_t                  i;\n+    ngx_array_t                *cookies;\n+    ngx_table_elt_t            *h;\n+    ngx_http_header_t          *hh;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    static ngx_str_t cookie = ngx_string(\"cookie\");\n+\n+    cookies = r->qstream->cookies;\n+\n+    if (cookies == NULL) {\n+        return NGX_OK;\n+    }\n+\n+    vals = cookies->elts;\n+\n+    i = 0;\n+    len = 0;\n+\n+    do {\n+        len += vals[i].len + 2;\n+    } while (++i != cookies->nelts);\n+\n+    len -= 2;\n+\n+    buf = ngx_pnalloc(r->pool, len + 1);\n+    if (buf == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    p = buf;\n+    end = buf + len;\n+\n+    for (i = 0; /* void */ ; i++) {\n+\n+        p = ngx_cpymem(p, vals[i].data, vals[i].len);\n+\n+        if (p == end) {\n+            *p = '\\0';\n+            break;\n+        }\n+\n+        *p++ = ';'; *p++ = ' ';\n+    }\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(\n+                                    ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e');\n+\n+    h->key.len = cookie.len;\n+    h->key.data = cookie.data;\n+\n+    h->value.len = len;\n+    h->value.data = buf;\n+\n+    h->lowcase_key = cookie.data;\n+\n+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+    hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n+                       h->lowcase_key, h->key.len);\n+\n+    if (hh == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    if (hh->handler(r, h, hh->offset) != NGX_OK) {\n+        /*\n+         * request has been finalized already\n+         * in ngx_http_process_multi_header_lines()\n+         */\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_run_request(ngx_http_request_t *r)\n+{\n+    if (ngx_http_v3_construct_request_line(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    if (ngx_http_v3_construct_cookie_header(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;\n+\n+    if (ngx_http_process_request_header(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    if (r->headers_in.content_length_n > 0 && r->qstream->in_closed) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client prematurely closed stream\");\n+\n+        r->qstream->skip_data = 1;\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return;\n+    }\n+\n+    if (r->headers_in.content_length_n == -1 && !r->qstream->in_closed) {\n+        r->headers_in.chunked = 1;\n+    }\n+\n+    ngx_http_process_request(r);\n+}\n+\n+\n+/* End of functions copied from HTTP/2 module. */\n+\n+\n+ngx_int_t\n+ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n+{\n+    size_t                     size;\n+    ngx_int_t                  rc;\n+    ngx_buf_t                 *b;\n+    ngx_chain_t               *cl, *tl, *out, **ll;\n+    ngx_connection_t          *c;\n+    ngx_http_request_body_t   *rb;\n+    ngx_http_core_loc_conf_t  *clcf;\n+\n+    c = r->qstream->connection->connection;\n+\n+    rb = r->request_body;\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    if (rb->rest == -1) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"http3 request body filter\");\n+\n+        if (r->headers_in.chunked) {\n+            rb->rest = clcf->client_body_buffer_size;\n+            r->headers_in.content_length_n = 0;\n+        } else {\n+            rb->rest = r->headers_in.content_length_n;\n+        }\n+    }\n+\n+    out = NULL;\n+    ll = &out;\n+\n+    for (cl = in; cl; cl = cl->next) {\n+\n+        if (rb->rest == 0) {\n+            break;\n+        }\n+\n+        if (ngx_buf_size(cl->buf) == 0) {\n+            continue;\n+        }\n+\n+        tl = ngx_chain_get_free_buf(r->pool, &rb->free);\n+        if (tl == NULL) {\n+            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n+        }\n+\n+        b = tl->buf;\n+\n+        ngx_memzero(b, sizeof(ngx_buf_t));\n+\n+        b->temporary = 1;\n+        b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;\n+        b->start = cl->buf->pos;\n+        b->pos = cl->buf->pos;\n+        b->last = cl->buf->last;\n+        b->end = cl->buf->end;\n+        b->flush = r->request_body_no_buffering;\n+\n+        size = cl->buf->last - cl->buf->pos;\n+\n+        cl->buf->pos = cl->buf->last;\n+\n+        if (r->headers_in.chunked) {\n+            r->headers_in.content_length_n += size;\n+        }\n+\n+        if (quiche_conn_stream_finished(c->quic->conn, r->qstream->id)) {\n+            rb->rest = 0;\n+            b->last = cl->buf->pos;\n+            b->last_buf = 1;\n+        }\n+\n+        *ll = tl;\n+        ll = &tl->next;\n+    }\n+\n+    rc = ngx_http_top_request_body_filter(r, out);\n+\n+    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,\n+                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);\n+\n+    return rc;\n+}\n+\n+\n+size_t\n+ngx_http_v3_get_headers_out_count(ngx_http_request_t *r)\n+{\n+    size_t                     headers_count;\n+    ngx_uint_t                 i;\n+    ngx_list_part_t           *part;\n+    ngx_table_elt_t           *header;\n+\n+    headers_count = 1; /* :status */\n+\n+    if (r->headers_out.server == NULL) {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.date == NULL) {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.content_type.len) {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.content_length == NULL\n+        && r->headers_out.content_length_n >= 0)\n+    {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.last_modified == NULL\n+        && r->headers_out.last_modified_time != -1)\n+    {\n+        headers_count += 1;\n+    }\n+\n+    if (r->headers_out.location && r->headers_out.location->value.len) {\n+        headers_count += 1;\n+    }\n+\n+#if (NGX_HTTP_GZIP)\n+    if (r->gzip_vary) {\n+        headers_count += 1;\n+    }\n+#endif\n+\n+    part = &r->headers_out.headers.part;\n+    header = part->elts;\n+\n+    for (i = 0; /* void */; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            header = part->elts;\n+            i = 0;\n+        }\n+\n+        if (header[i].hash == 0) {\n+            continue;\n+        }\n+\n+        headers_count += 1;\n+    }\n+\n+    return headers_count;\n+}\n+\n+\n+ngx_int_t\n+ngx_http_v3_push_response_headers(ngx_http_request_t *r)\n+{\n+    u_char                    *tmp;\n+    size_t                     len, headers_count;\n+    ngx_str_t                  host, location;\n+    ngx_uint_t                 i, port;\n+    ngx_list_part_t           *part;\n+    ngx_table_elt_t           *header;\n+    ngx_connection_t          *fc;\n+    quiche_h3_header          *h;\n+    ngx_http_core_loc_conf_t  *clcf;\n+    ngx_http_core_srv_conf_t  *cscf;\n+    u_char                     addr[NGX_SOCKADDR_STRLEN];\n+\n+    /* The list of response headers was already generated, so there's nothing\n+     * more to do here. */\n+    if (r->qstream->headers != NULL) {\n+        return NGX_OK;\n+    }\n+\n+    fc = r->connection;\n+\n+    if (r->method == NGX_HTTP_HEAD) {\n+        r->header_only = 1;\n+    }\n+\n+    switch (r->headers_out.status) {\n+\n+    case NGX_HTTP_OK:\n+        break;\n+\n+    case NGX_HTTP_NO_CONTENT:\n+        r->header_only = 1;\n+\n+        ngx_str_null(&r->headers_out.content_type);\n+\n+        r->headers_out.content_length = NULL;\n+        r->headers_out.content_length_n = -1;\n+\n+        r->headers_out.last_modified_time = -1;\n+        r->headers_out.last_modified = NULL;\n+        break;\n+\n+    case NGX_HTTP_PARTIAL_CONTENT:\n+        break;\n+\n+    case NGX_HTTP_NOT_MODIFIED:\n+        r->header_only = 1;\n+        break;\n+\n+    default:\n+        r->headers_out.last_modified_time = -1;\n+        r->headers_out.last_modified = NULL;\n+    }\n+\n+    headers_count = ngx_http_v3_get_headers_out_count(r);\n+\n+    r->qstream->headers =\n+        ngx_array_create(r->pool, headers_count, sizeof(quiche_h3_header));\n+\n+    if (r->qstream->headers == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* Generate :status pseudo-header. */\n+    {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \":status\";\n+        h->name_len = sizeof(\":status\") - 1;\n+\n+        tmp = ngx_pnalloc(r->pool, sizeof(\"418\") - 1);\n+        if (tmp == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->value = tmp;\n+        h->value_len = ngx_sprintf(tmp, \"%03ui\", r->headers_out.status) - tmp;\n+    }\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    /* Generate Server header.*/\n+    if (r->headers_out.server == NULL) {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"server\";\n+        h->name_len = sizeof(\"server\") - 1;\n+\n+        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n+            h->value = (u_char *) NGINX_VER;\n+            h->value_len = sizeof(NGINX_VER) - 1;\n+\n+        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n+            h->value = (u_char *) NGINX_VER_BUILD;\n+            h->value_len = sizeof(NGINX_VER_BUILD) - 1;\n+\n+        } else {\n+            h->value = (u_char *) \"nginx\";\n+            h->value_len = sizeof(\"nginx\") - 1;\n+        }\n+    }\n+\n+    /* Generate Date header. */\n+    if (r->headers_out.date == NULL) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"date: %V\\\"\",\n+                       &ngx_cached_http_time);\n+\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"date\";\n+        h->name_len = sizeof(\"date\") - 1;\n+\n+        h->value = ngx_cached_http_time.data;\n+        h->value_len = ngx_cached_http_time.len;\n+    }\n+\n+    /* Generate Content-Type header. */\n+    if (r->headers_out.content_type.len) {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n+            && r->headers_out.charset.len)\n+        {\n+            len = r->headers_out.content_type.len + sizeof(\"; charset=\") - 1\n+                  + r->headers_out.charset.len;\n+\n+            tmp = ngx_pnalloc(r->pool, len);\n+            if (tmp == NULL) {\n+                return NGX_ERROR;\n+            }\n+\n+            tmp = ngx_cpymem(tmp, r->headers_out.content_type.data,\n+                             r->headers_out.content_type.len);\n+\n+            tmp = ngx_cpymem(tmp, \"; charset=\", sizeof(\"; charset=\") - 1);\n+\n+            tmp = ngx_cpymem(tmp, r->headers_out.charset.data,\n+                             r->headers_out.charset.len);\n+\n+            /* updated r->headers_out.content_type is also needed for logging */\n+\n+            r->headers_out.content_type.len = len;\n+            r->headers_out.content_type.data = tmp - len;\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"content-type: %V\\\"\",\n+                       &r->headers_out.content_type);\n+\n+        h->name = (u_char *) \"content-type\";\n+        h->name_len = sizeof(\"content-type\") - 1;\n+\n+        h->value = r->headers_out.content_type.data;\n+        h->value_len = r->headers_out.content_type.len;\n+    }\n+\n+    /* Generate Content-Length header. */\n+    if (r->headers_out.content_length == NULL\n+        && r->headers_out.content_length_n >= 0)\n+    {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"content-length\";\n+        h->name_len = sizeof(\"content-length\") - 1;\n+\n+        tmp = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n+        if (tmp == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->value = tmp;\n+        h->value_len =\n+            ngx_sprintf(tmp, \"%O\", r->headers_out.content_length_n) - tmp;\n+    }\n+\n+    /* Generate Last-Modified header. */\n+    if (r->headers_out.last_modified == NULL\n+        && r->headers_out.last_modified_time != -1)\n+    {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"last-modified\";\n+        h->name_len = sizeof(\"last-modified\") - 1;\n+\n+        tmp = ngx_pnalloc(r->pool, sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1);\n+        if (tmp == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->value = tmp;\n+        h->value_len =\n+            ngx_http_time(tmp, r->headers_out.last_modified_time) - tmp;\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"last-modified: %*.s\\\"\",\n+                       h->value_len, h->value);\n+    }\n+\n+    /* Generate Location header. */\n+    if (r->headers_out.location && r->headers_out.location->value.len) {\n+\n+        if (r->headers_out.location->value.data[0] == '/'\n+            && clcf->absolute_redirect)\n+        {\n+            if (clcf->server_name_in_redirect) {\n+                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+                host = cscf->server_name;\n+\n+            } else if (r->headers_in.server.len) {\n+                host = r->headers_in.server;\n+\n+            } else {\n+                host.data = addr;\n+                host.len = NGX_SOCKADDR_STRLEN;\n+\n+                if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {\n+                    return NGX_ERROR;\n+                }\n+            }\n+\n+            port = ngx_inet_get_port(fc->local_sockaddr);\n+\n+            location.len = sizeof(\"https://\") - 1 + host.len\n+                           + r->headers_out.location->value.len;\n+\n+            if (clcf->port_in_redirect) {\n+\n+#if (NGX_HTTP_SSL)\n+                if (fc->ssl)\n+                    port = (port == 443) ? 0 : port;\n+                else\n+#endif\n+                    port = (port == 80) ? 0 : port;\n+\n+            } else {\n+                port = 0;\n+            }\n+\n+            if (port) {\n+                location.len += sizeof(\":65535\") - 1;\n+            }\n+\n+            location.data = ngx_pnalloc(r->pool, location.len);\n+            if (location.data == NULL) {\n+                return NGX_ERROR;\n+            }\n+\n+            tmp = ngx_cpymem(location.data, \"http\", sizeof(\"http\") - 1);\n+\n+#if (NGX_HTTP_SSL)\n+            if (fc->ssl) {\n+                *tmp++ = 's';\n+            }\n+#endif\n+\n+            *tmp++ = ':'; *tmp++ = '/'; *tmp++ = '/';\n+            tmp = ngx_cpymem(tmp, host.data, host.len);\n+\n+            if (port) {\n+                tmp = ngx_sprintf(tmp, \":%ui\", port);\n+            }\n+\n+            tmp = ngx_cpymem(tmp, r->headers_out.location->value.data,\n+                                  r->headers_out.location->value.len);\n+\n+            /* update r->headers_out.location->value for possible logging */\n+\n+            r->headers_out.location->value.len = tmp - location.data;\n+            r->headers_out.location->value.data = location.data;\n+            ngx_str_set(&r->headers_out.location->key, \"Location\");\n+        }\n+\n+        r->headers_out.location->hash = 0;\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"location: %V\\\"\",\n+                       &r->headers_out.location->value);\n+\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"location\";\n+        h->name_len = sizeof(\"location\") - 1;\n+\n+        h->value = r->headers_out.location->value.data;\n+        h->value_len = r->headers_out.location->value.len;\n+    }\n+\n+#if (NGX_HTTP_GZIP)\n+    /* Generate Vary header. */\n+    if (r->gzip_vary) {\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"vary: Accept-Encoding\\\"\");\n+\n+        h->name = (u_char *) \"vary\";\n+        h->name_len = sizeof(\"vary\") - 1;\n+\n+        h->value = (u_char *) \"Accept-Encoding\";\n+        h->value_len = sizeof(\"Accept-Encoding\") - 1;\n+    }\n+#endif\n+\n+    part = &r->headers_out.headers.part;\n+    header = part->elts;\n+\n+    /* Generate all other headers. */\n+    for (i = 0; /* void */; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            header = part->elts;\n+            i = 0;\n+        }\n+\n+        if (header[i].hash == 0) {\n+            continue;\n+        }\n+\n+        h = ngx_array_push(r->qstream->headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+#if (NGX_DEBUG)\n+        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                           \"http3 output header: \\\"%V: %V\\\"\",\n+                           &header[i].key, &header[i].value);\n+        }\n+#endif\n+\n+        h->name = header[i].key.data;\n+        h->name_len = header[i].key.len;\n+\n+        h->value = header[i].value.data;\n+        h->value_len = header[i].value.len;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_http_v3_send_response(ngx_http_request_t *r)\n+{\n+    int                        rc;\n+    ngx_uint_t                 fin;\n+    ngx_connection_t          *c, *fc;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 send response stream %ui\", r->qstream->id);\n+\n+    fc = r->connection;\n+\n+    if (fc->error) {\n+        return NGX_ERROR;\n+    }\n+\n+    h3c = r->qstream->connection;\n+    c = h3c->connection;\n+\n+    if (ngx_http_v3_push_response_headers(r) != NGX_OK) {\n+        return NGX_ERROR;\n+    }\n+\n+    fin = r->header_only\n+          || (r->headers_out.content_length_n == 0 && !r->expect_trailers);\n+\n+    rc = quiche_h3_send_response(h3c->h3, c->quic->conn, r->qstream->id,\n+                                 r->qstream->headers->elts,\n+                                 r->qstream->headers->nelts,\n+                                 fin);\n+\n+    if (rc == QUICHE_H3_ERR_STREAM_BLOCKED) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"http3 stream blocked %ui\", r->qstream->id);\n+\n+        r->qstream->blocked = 1;\n+\n+        fc->write->active = 1;\n+        fc->write->ready = 0;\n+\n+        return NGX_AGAIN;\n+    }\n+\n+    if (rc != NGX_OK) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (fin) {\n+        r->qstream->out_closed = 1;\n+    }\n+\n+    r->qstream->headers_sent = 1;\n+\n+    if (r->done) {\n+        fc->write->handler = ngx_http_v3_close_stream_handler;\n+        fc->read->handler = ngx_http_empty_handler;\n+    }\n+\n+    ngx_post_event(c->write, &ngx_posted_events);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ssize_t\n+ngx_http_v3_stream_do_send(ngx_connection_t *fc, ngx_buf_t *b, ngx_int_t fin)\n+{\n+    ssize_t                    n;\n+    ngx_connection_t          *c;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_connection_t  *h3c;\n+    ngx_http_v3_stream_t      *stream;\n+\n+    uint8_t *buf = b ? b->pos : NULL;\n+    size_t buf_len = b ? ngx_buf_size(b) : 0;\n+\n+    r = fc->data;\n+    stream = r->qstream;\n+    h3c = stream->connection;\n+    c = h3c->connection;\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, fc->log, 0,\n+                   \"http3 stream %uz to write %uz bytes, fin=%d\",\n+                   stream->id, buf_len, fin);\n+\n+    if (!stream->headers_sent) {\n+        return NGX_AGAIN;\n+    }\n+\n+    n = quiche_h3_send_body(h3c->h3, c->quic->conn, r->qstream->id,\n+                            buf, buf_len, fin);\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, stream->connection->connection->log, 0,\n+                   \"http3 stream written %z bytes\", n);\n+\n+    if (n == QUICHE_H3_ERR_DONE) {\n+        return NGX_AGAIN;\n+    }\n+\n+    if (n < 0) {\n+        ngx_log_error(NGX_LOG_ERR, fc->log, 0, \"stream write failed: %d\", n);\n+        return NGX_ERROR;\n+    }\n+\n+    return n;\n+}\n+\n+\n+static ssize_t\n+ngx_http_v3_recv_body(ngx_connection_t *c, u_char *buf, size_t size)\n+{\n+    ssize_t                    n;\n+    ngx_event_t               *rev;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    rev = c->read;\n+\n+    r = c->data;\n+    h3c = r->qstream->connection;\n+\n+    if (c->error) {\n+        rev->ready = 0;\n+\n+        return NGX_ERROR;\n+    }\n+\n+    n = quiche_h3_recv_body(h3c->h3, c->quic->conn, r->qstream->id, buf, size);\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                   \"http3 body recv: %z of %uz\", n, size);\n+\n+    if (quiche_conn_stream_finished(c->quic->conn, r->qstream->id)) {\n+        rev->ready = 0;\n+\n+        /* Re-schedule connection read event to poll for Finished event. */\n+        ngx_post_event(h3c->connection->read, &ngx_posted_events);\n+    }\n+\n+    if (n == 0) {\n+        rev->ready = 0;\n+\n+        return 0;\n+    }\n+\n+    if (n > 0) {\n+\n+        if ((size_t) n < size) {\n+            rev->ready = 0;\n+        }\n+\n+        return n;\n+    }\n+\n+    if (n == QUICHE_H3_ERR_DONE) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quiche_h3_recv_body() not ready\");\n+\n+        n = NGX_AGAIN;\n+\n+    } else {\n+        rev->error = 1;\n+\n+        n = NGX_ERROR;\n+    }\n+\n+    rev->ready = 0;\n+\n+    return n;\n+}\n+\n+\n+static ngx_chain_t *\n+ngx_http_v3_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)\n+{\n+    ssize_t                n, sent;\n+    off_t                  send, prev_send;\n+    ngx_uint_t             blocked, fin;\n+\n+    ngx_http_request_t    *r;\n+    ngx_http_v3_stream_t  *stream;\n+\n+    r = fc->data;\n+    stream = r->qstream;\n+\n+    send = 0;\n+\n+    blocked = 0;\n+\n+    while (in) {\n+        off_t size = ngx_buf_size(in->buf);\n+\n+        if (size || in->buf->last_buf) {\n+            break;\n+        }\n+\n+        in = in->next;\n+    }\n+\n+    if (in == NULL || stream->out_closed) {\n+        return NULL;\n+    }\n+\n+    while (in) {\n+        prev_send = send;\n+\n+        fin = in->buf->last_buf;\n+\n+        send += ngx_buf_size(in->buf);\n+\n+        n = ngx_http_v3_stream_do_send(fc, in->buf, fin);\n+\n+        if (n == NGX_ERROR) {\n+            return NGX_CHAIN_ERROR;\n+        }\n+\n+        sent = (n == NGX_AGAIN) ? 0 : n;\n+\n+        fc->sent += sent;\n+\n+        in->buf->pos += sent;\n+\n+        /* Partial (or no) write, end now. */\n+        if ((n == NGX_AGAIN) || (send - prev_send != sent)) {\n+            blocked = 1;\n+            break;\n+        }\n+\n+        /* Buffer is fully written, switch to the next. */\n+        if (in->buf->pos == in->buf->last) {\n+            in = in->next;\n+        }\n+\n+        if (fin) {\n+            stream->out_closed = 1;\n+        }\n+    }\n+\n+    if (blocked) {\n+        if (!stream->blocked) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, stream->connection->connection->log, 0,\n+                           \"http3 stream blocked %ui\", stream->id);\n+\n+            stream->blocked = 1;\n+\n+            fc->write->active = 1;\n+            fc->write->ready = 0;\n+        }\n+    }\n+\n+    ngx_post_event(stream->connection->connection->write, &ngx_posted_events);\n+\n+    return in;\n+}\n+\n+\n+void\n+ngx_http_v3_close_stream(ngx_http_v3_stream_t *stream, ngx_int_t rc)\n+{\n+    ngx_event_t               *ev;\n+    ngx_connection_t          *fc;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    h3c = stream->connection;\n+\n+    fc = stream->request->connection;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 close stream %ui\", stream->id);\n+\n+    if (stream->blocked) {\n+        fc->write->handler = ngx_http_v3_close_stream_handler;\n+        fc->read->handler = ngx_http_empty_handler;\n+        return;\n+    }\n+\n+    quiche_conn_stream_shutdown(h3c->connection->quic->conn, stream->id,\n+                                QUICHE_SHUTDOWN_READ, 0);\n+\n+    ngx_rbtree_delete(&h3c->streams, &stream->node);\n+\n+    ngx_http_free_request(stream->request, rc);\n+\n+    ev = fc->read;\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    ev = fc->write;\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    fc->data = h3c->free_fake_connections;\n+    h3c->free_fake_connections = fc;\n+\n+    h3c->processing--;\n+\n+    ngx_http_v3_handle_connection(h3c);\n+}\n+\n+\n+static void\n+ngx_http_v3_close_stream_handler(ngx_event_t *ev)\n+{\n+    ngx_connection_t    *fc;\n+    ngx_http_request_t  *r;\n+\n+    fc = ev->data;\n+    r = fc->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                   \"http3 close stream handler\");\n+\n+    if (ev->timedout) {\n+        ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, \"client timed out\");\n+\n+        fc->timedout = 1;\n+\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_REQUEST_TIME_OUT);\n+        return;\n+    }\n+\n+    ngx_http_v3_close_stream(r->qstream, 0);\n+}\n+\n+void\n+ngx_http_v3_stop_stream_read(ngx_http_v3_stream_t *stream, ngx_int_t rc)\n+{\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    if (!stream) {\n+        return;\n+    }\n+\n+    h3c = stream->connection;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 stream shutdown read %ui\", stream->id);\n+\n+    quiche_conn_stream_shutdown(h3c->connection->quic->conn,\n+                                stream->id,\n+                                QUICHE_SHUTDOWN_READ, rc);\n+}\n+\n+\n+static void\n+ngx_http_v3_finalize_connection(ngx_http_v3_connection_t *h3c,\n+    ngx_uint_t status)\n+{\n+    ngx_event_t             *ev;\n+    ngx_connection_t        *c, *fc;\n+    ngx_rbtree_node_t       *node, *root, *sentinel;\n+    ngx_http_request_t      *r;\n+    ngx_http_v3_stream_t    *stream;\n+\n+    c = h3c->connection;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 finalize connection\");\n+\n+    quiche_conn_close(c->quic->conn, true, status, NULL, 0);\n+\n+    c->error = 1;\n+\n+    if (!h3c->processing) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    c->read->handler = ngx_http_empty_handler;\n+    c->write->handler = ngx_http_empty_handler;\n+\n+    root = h3c->streams.root;\n+    sentinel = h3c->streams.sentinel;\n+\n+    if (root != sentinel) {\n+        node = ngx_rbtree_min(h3c->streams.root, sentinel);\n+    } else {\n+        node = NULL;\n+    }\n+\n+    /* Close all pending streams / requests. */\n+    while (node != NULL) {\n+        stream = (ngx_http_v3_stream_t *) node;\n+\n+        r = stream->request;\n+        fc = r->connection;\n+\n+        fc->error = 1;\n+\n+        if (c->close) {\n+            fc->close = 1;\n+        }\n+\n+        if (stream->blocked) {\n+            stream->blocked = 0;\n+\n+            ev = fc->write;\n+            ev->active = 0;\n+            ev->ready = 1;\n+\n+        } else {\n+            ev = fc->read;\n+        }\n+\n+        node = ngx_rbtree_next(&h3c->streams, node);\n+\n+        ev->eof = 1;\n+        ev->handler(ev);\n+    }\n+\n+    if (h3c->processing) {\n+        return;\n+    }\n+\n+    ngx_http_close_connection(c);\n+}\n+\n+\n+static void\n+ngx_http_v3_pool_cleanup(void *data)\n+{\n+    ngx_http_v3_connection_t  *h3c = data;\n+\n+    if (h3c->h3) {\n+        quiche_h3_conn_free(h3c->h3);\n+\n+        h3c->h3 = NULL;\n+    }\n+}\ndiff --color -uNr a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c\n--- a/src/http/v3/ngx_http_v3_filter_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3_filter_module.c\t2021-09-09 19:04:29.398062898 +0800\n@@ -0,0 +1,68 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+\n+static ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf);\n+\n+\n+static ngx_http_module_t  ngx_http_v3_filter_module_ctx = {\n+    NULL,                                  /* preconfiguration */\n+    ngx_http_v3_filter_init,               /* postconfiguration */\n+\n+    NULL,                                  /* create main configuration */\n+    NULL,                                  /* init main configuration */\n+\n+    NULL,                                  /* create server configuration */\n+    NULL,                                  /* merge server configuration */\n+\n+    NULL,                                  /* create location configuration */\n+    NULL                                   /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_v3_filter_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_v3_filter_module_ctx,        /* module context */\n+    NULL,                                  /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\n+\n+\n+static ngx_int_t\n+ngx_http_v3_header_filter(ngx_http_request_t *r)\n+{\n+    if (!r->qstream) {\n+        return ngx_http_next_header_filter(r);\n+    }\n+\n+    return ngx_http_v3_send_response(r);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_filter_init(ngx_conf_t *cf)\n+{\n+    ngx_http_next_header_filter = ngx_http_top_header_filter;\n+    ngx_http_top_header_filter = ngx_http_v3_header_filter;\n+\n+    return NGX_OK;\n+}\ndiff --color -uNr a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h\n--- a/src/http/v3/ngx_http_v3.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3.h\t2021-09-09 19:04:29.398062898 +0800\n@@ -0,0 +1,79 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#ifndef _NGX_HTTP_V3_H_INCLUDED_\n+#define _NGX_HTTP_V3_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+\n+#define NGX_HTTP_V3_ALPN_ADVERTISE       \"\\x05h3-18\"\n+\n+\n+typedef struct ngx_http_v3_connection_s   ngx_http_v3_connection_t;\n+\n+\n+struct ngx_http_v3_connection_s {\n+    quiche_h3_conn             *h3;\n+\n+    ngx_connection_t           *connection;\n+    ngx_http_connection_t      *http_connection;\n+\n+    ngx_pool_t                 *pool;\n+\n+    ngx_uint_t                  processing;\n+\n+    ngx_rbtree_t                streams;\n+    ngx_rbtree_node_t           streams_sentinel;\n+\n+    ngx_connection_t           *free_fake_connections;\n+};\n+\n+\n+struct ngx_http_v3_stream_s {\n+    ngx_rbtree_node_t          node;\n+\n+    uint64_t                   id;\n+\n+    ngx_http_request_t        *request;\n+\n+    ngx_http_v3_connection_t  *connection;\n+\n+    ngx_array_t               *headers;\n+    ngx_array_t               *cookies;\n+\n+    ngx_http_v3_stream_t      *next;\n+\n+    ngx_uint_t                 headers_sent:1;\n+    ngx_uint_t                 in_closed:1;\n+    ngx_uint_t                 out_closed:1;\n+    ngx_uint_t                 skip_data:1;\n+    ngx_uint_t                 blocked:1;\n+};\n+\n+\n+typedef struct {\n+    ngx_str_t                        name;\n+    ngx_str_t                        value;\n+} ngx_http_v3_header_t;\n+\n+\n+void ngx_http_v3_init(ngx_event_t *rev);\n+\n+ngx_int_t ngx_http_v3_send_response(ngx_http_request_t *r);\n+\n+void ngx_http_v3_close_stream(ngx_http_v3_stream_t *stream, ngx_int_t rc);\n+void ngx_http_v3_stop_stream_read(ngx_http_v3_stream_t *stream, ngx_int_t rc);\n+\n+ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r,\n+    ngx_chain_t *in);\n+\n+\n+#endif /* _NGX_HTTP_V3_H_INCLUDED_ */\ndiff --color -uNr a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c\n--- a/src/http/v3/ngx_http_v3_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3_module.c\t2021-09-09 19:04:29.398062898 +0800\n@@ -0,0 +1,286 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+#include <quiche.h>\n+\n+\n+static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf);\n+\n+static void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf);\n+static char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf,\n+    void *parent, void *child);\n+\n+static ngx_int_t ngx_http_v3_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data);\n+\n+static void ngx_http_v3_cleanup_ctx(void *data);\n+\n+\n+static ngx_command_t  ngx_http_v3_commands[] = {\n+\n+    { ngx_string(\"http3_max_concurrent_streams\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, concurrent_streams),\n+      NULL },\n+\n+    { ngx_string(\"http3_max_requests\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_requests),\n+      NULL },\n+\n+    { ngx_string(\"http3_max_header_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_header_size),\n+      NULL },\n+\n+    { ngx_string(\"http3_initial_max_data\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_data),\n+      NULL },\n+\n+    { ngx_string(\"http3_initial_max_stream_data\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_stream_data),\n+      NULL },\n+\n+    { ngx_string(\"http3_idle_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, idle_timeout),\n+      NULL },\n+\n+      ngx_null_command\n+};\n+\n+\n+static ngx_http_module_t  ngx_http_v3_module_ctx = {\n+    ngx_http_v3_add_variables,             /* preconfiguration */\n+    NULL,                                  /* postconfiguration */\n+\n+    NULL,                                  /* create main configuration */\n+    NULL,                                  /* init main configuration */\n+\n+    ngx_http_v3_create_srv_conf,           /* create server configuration */\n+    ngx_http_v3_merge_srv_conf,            /* merge server configuration */\n+\n+    NULL,                                  /* create location configuration */\n+    NULL                                   /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_v3_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_v3_module_ctx,             /* module context */\n+    ngx_http_v3_commands,                /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_variable_t ngx_http_v3_variables[] = {\n+\n+    { ngx_string(\"http3\"), NULL,\n+      ngx_http_v3_variable, 0,\n+      NGX_HTTP_VAR_CHANGEABLE, 0 },\n+\n+      ngx_http_null_variable\n+};\n+\n+\n+static ngx_int_t\n+ngx_http_v3_add_variables(ngx_conf_t *cf)\n+{\n+    ngx_http_variable_t *var, *v;\n+\n+    for (v = ngx_http_v3_variables; v->name.len; v++) {\n+        var = ngx_http_add_variable(cf, &v->name, v->flags);\n+        if (var == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        var->get_handler = v->get_handler;\n+        var->data = v->data;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void *\n+ngx_http_v3_create_srv_conf(ngx_conf_t *cf)\n+{\n+    ngx_http_v3_srv_conf_t  *h3scf;\n+\n+    h3scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t));\n+    if (h3scf == NULL) {\n+        return NULL;\n+    }\n+\n+    h3scf->idle_timeout = NGX_CONF_UNSET_MSEC;\n+    h3scf->max_data = NGX_CONF_UNSET_SIZE;\n+    h3scf->max_stream_data = NGX_CONF_UNSET_SIZE;\n+    h3scf->max_requests = NGX_CONF_UNSET_UINT;\n+    h3scf->max_header_size = NGX_CONF_UNSET_SIZE;\n+    h3scf->concurrent_streams = NGX_CONF_UNSET_UINT;\n+\n+    return h3scf;\n+}\n+\n+\n+#if (NGX_DEBUG)\n+static void\n+quiche_log(const char *line, void *argp)\n+{\n+    ngx_log_t *log = ngx_cycle->log;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, \"%s\", line);\n+}\n+#endif\n+\n+\n+static char *\n+ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n+{\n+    ngx_http_v3_srv_conf_t *prev = parent;\n+    ngx_http_v3_srv_conf_t *conf = child;\n+\n+    ngx_pool_cleanup_t  *cln;\n+\n+    ngx_conf_merge_msec_value(conf->idle_timeout,\n+                              prev->idle_timeout, 180000);\n+\n+    ngx_conf_merge_size_value(conf->max_data,\n+                              prev->max_data, 10485760);\n+\n+    ngx_conf_merge_size_value(conf->max_stream_data,\n+                              prev->max_stream_data, 1048576);\n+\n+    ngx_conf_merge_uint_value(conf->max_requests,\n+                              prev->max_requests, 1000);\n+\n+    ngx_conf_merge_size_value(conf->max_header_size,\n+                              prev->max_header_size, 16384);\n+\n+    ngx_conf_merge_uint_value(conf->concurrent_streams,\n+                              prev->concurrent_streams, 128);\n+\n+    conf->quic.log = cf->log;\n+\n+#if (NGX_DEBUG)\n+    /* Enable quiche debug logging. quiche commit ceade4 or later is required */\n+    quiche_enable_debug_logging(quiche_log, NULL);\n+#endif\n+\n+    if (ngx_quic_create_conf(&conf->quic) != NGX_OK) {\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    quiche_config_set_max_idle_timeout(conf->quic.config, conf->idle_timeout);\n+\n+    quiche_config_set_initial_max_data(conf->quic.config, conf->max_data);\n+\n+    quiche_config_set_initial_max_stream_data_bidi_remote(conf->quic.config,\n+                                                          conf->max_stream_data);\n+\n+    quiche_config_set_initial_max_stream_data_uni(conf->quic.config,\n+                                                  conf->max_stream_data);\n+\n+    quiche_config_set_initial_max_streams_bidi(conf->quic.config,\n+                                               conf->concurrent_streams);\n+\n+    /* For HTTP/3 we only need 3 unidirectional streams. */\n+    quiche_config_set_initial_max_streams_uni(conf->quic.config, 3);\n+\n+    conf->http3 = quiche_h3_config_new();\n+    if (conf->http3 == NULL) {\n+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                           \"failed to create HTTP/3 config\");\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    quiche_h3_config_set_max_field_section_size(conf->http3,\n+                                                conf->max_header_size);\n+\n+    cln = ngx_pool_cleanup_add(cf->pool, 0);\n+    if (cln == NULL) {\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    cln->handler = ngx_quic_cleanup_ctx;\n+    cln->data = &conf->quic;\n+\n+    cln = ngx_pool_cleanup_add(cf->pool, 0);\n+    if (cln == NULL) {\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    cln->handler = ngx_http_v3_cleanup_ctx;\n+    cln->data = conf->http3;\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n+    uintptr_t data)\n+{\n+    ngx_connection_t   *c;\n+\n+    v->valid = 1;\n+    v->no_cacheable = 1;\n+    v->not_found = 0;\n+\n+    c = r->connection;\n+    if (c == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (c->quic != NULL) {\n+        v->len = sizeof(\"h3\") - 1;\n+        v->valid = 1;\n+        v->no_cacheable = 0;\n+        v->not_found = 0;\n+        v->data = (u_char *) \"h3\";\n+\n+        return NGX_OK;\n+    }\n+\n+    *v = ngx_http_variable_null_value;\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_cleanup_ctx(void *data)\n+{\n+    quiche_h3_config  *config = data;\n+\n+    quiche_h3_config_free(config);\n+}\ndiff --color -uNr a/src/http/v3/ngx_http_v3_module.h b/src/http/v3/ngx_http_v3_module.h\n--- a/src/http/v3/ngx_http_v3_module.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3_module.h\t2021-09-09 19:04:29.398062898 +0800\n@@ -0,0 +1,34 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#ifndef _NGX_HTTP_V3_MODULE_H_INCLUDED_\n+#define _NGX_HTTP_V3_MODULE_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+\n+#include <quiche.h>\n+\n+\n+typedef struct {\n+    ngx_quic_t                      quic;\n+\n+    quiche_h3_config                *http3;\n+\n+    ngx_msec_t                      idle_timeout;\n+    size_t                          max_data;\n+    size_t                          max_stream_data;\n+    ngx_uint_t                      max_requests;\n+    ngx_uint_t                      max_header_size;\n+    ngx_uint_t                      concurrent_streams;\n+} ngx_http_v3_srv_conf_t;\n+\n+\n+extern ngx_module_t  ngx_http_v3_module;\n+\n+\n+#endif /* _NGX_HTTP_V3_MODULE_H_INCLUDED_ */\ndiff --color -uNr a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_chain.c\n--- a/src/os/unix/ngx_udp_sendmsg_chain.c\t2021-09-07 23:21:03.000000000 +0800\n+++ b/src/os/unix/ngx_udp_sendmsg_chain.c\t2021-09-09 19:04:29.398062898 +0800\n@@ -322,6 +322,7 @@\n \n         switch (err) {\n         case NGX_EAGAIN:\n+        case ENOBUFS:\n             ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                            \"sendmsg() not ready\");\n             return NGX_AGAIN;\n"
  },
  {
    "path": "nginx_with_spdy.patch",
    "content": "Add SPDY Support.\nAdd HTTP2 HPACK Encoding Support.\nAdd Dynamic TLS Record support.\n\nUsing: patch -p1 < nginx.patch\n\ndiff -uNr a/auto/modules b/auto/modules\n--- a/auto/modules\t2019-09-24 23:08:48.000000000 +0800\n+++ b/auto/modules\t2019-10-17 22:51:22.603255482 +0800\n@@ -119,6 +119,7 @@\n     #     ngx_http_header_filter\n     #     ngx_http_chunked_filter\n     #     ngx_http_v2_filter\n+    #     ngx_http_spdy_filter\n     #     ngx_http_range_header_filter\n     #     ngx_http_gzip_filter\n     #     ngx_http_postpone_filter\n@@ -151,6 +152,7 @@\n                       ngx_http_header_filter_module \\\n                       ngx_http_chunked_filter_module \\\n                       ngx_http_v2_filter_module \\\n+                      ngx_http_spdy_filter_module \\\n                       ngx_http_range_header_filter_module \\\n                       ngx_http_gzip_filter_module \\\n                       ngx_http_postpone_filter_module \\\n@@ -212,6 +214,19 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_SPDY = YES ]; then\n+        have=NGX_HTTP_SPDY . auto/have\n+        USE_ZLIB=YES\n+        ngx_module_name=ngx_http_spdy_filter_module\n+        ngx_module_incs=\n+        ngx_module_deps=\n+        ngx_module_srcs=src/http/ngx_http_spdy_filter_module.c\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_SPDY\n+\n+        . auto/module\n+    fi\n+\n     if :; then\n         ngx_module_name=ngx_http_range_header_filter_module\n         ngx_module_incs=\n@@ -422,6 +437,23 @@\n \n         . auto/module\n     fi\n+\n+    if [ $HTTP_V2_HPACK_ENC = YES ]; then\n+        have=NGX_HTTP_V2_HPACK_ENC . auto/have\n+    fi\n+\n+    if [ $HTTP_SPDY = YES ]; then\n+        have=NGX_HTTP_SPDY . auto/have\n+        ngx_module_name=ngx_http_spdy_module\n+        ngx_module_incs=src/http\n+        ngx_module_deps=\"src/http/ngx_http_spdy.h src/http/ngx_http_spdy_module.h\"\n+        ngx_module_srcs=\"src/http/ngx_http_spdy.c \\\n+                         src/http/ngx_http_spdy_module.c\"\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_SPDY\n+\n+        . auto/module\n+    fi\n \n     if :; then\n         ngx_module_name=ngx_http_static_module\ndiff -uNr a/auto/options b/auto/options\n--- a/auto/options\t2019-09-24 23:08:48.000000000 +0800\n+++ b/auto/options\t2019-10-17 22:51:22.603255482 +0800\n@@ -58,7 +58,9 @@\n HTTP_CHARSET=YES\n HTTP_GZIP=YES\n HTTP_SSL=NO\n+HTTP_SPDY=NO\n HTTP_V2=NO\n+HTTP_V2_HPACK_ENC=NO\n HTTP_SSI=YES\n HTTP_REALIP=NO\n HTTP_XSLT=NO\n@@ -223,7 +225,9 @@\n         --http-scgi-temp-path=*)         NGX_HTTP_SCGI_TEMP_PATH=\"$value\" ;;\n \n         --with-http_ssl_module)          HTTP_SSL=YES               ;;\n+        --with-http_spdy_module)         HTTP_SPDY=YES              ;;\n         --with-http_v2_module)           HTTP_V2=YES                ;;\n+        --with-http_v2_hpack_enc)        HTTP_V2_HPACK_ENC=YES      ;;\n         --with-http_realip_module)       HTTP_REALIP=YES            ;;\n         --with-http_addition_module)     HTTP_ADDITION=YES          ;;\n         --with-http_xslt_module)         HTTP_XSLT=YES              ;;\n@@ -438,7 +442,9 @@\n   --with-file-aio                    enable file AIO support\n \n   --with-http_ssl_module             enable ngx_http_ssl_module\n+  --with-http_spdy_module            enable ngx_http_spdy_module\n   --with-http_v2_module              enable ngx_http_v2_module\n+  --with-http_v2_hpack_enc           enable ngx_http_v2_hpack_enc\n   --with-http_realip_module          enable ngx_http_realip_module\n   --with-http_addition_module        enable ngx_http_addition_module\n   --with-http_xslt_module            enable ngx_http_xslt_module\ndiff -uNr a/src/core/ngx_connection.h b/src/core/ngx_connection.h\n--- a/src/core/ngx_connection.h\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/core/ngx_connection.h\t2019-10-17 22:51:22.604255490 +0800\n@@ -119,6 +119,7 @@\n #define NGX_LOWLEVEL_BUFFERED  0x0f\n #define NGX_SSL_BUFFERED       0x01\n #define NGX_HTTP_V2_BUFFERED   0x02\n+#define NGX_SPDY_BUFFERED      0x04\n \n \n struct ngx_connection_s {\ndiff -uNr a/src/core/ngx_murmurhash.c b/src/core/ngx_murmurhash.c\n--- a/src/core/ngx_murmurhash.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/core/ngx_murmurhash.c\t2019-10-17 22:51:22.604255490 +0800\n@@ -50,3 +50,63 @@\n \n     return h;\n }\n+\n+\n+uint64_t\n+ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed)\n+{\n+    uint64_t  h, k;\n+\n+    h = seed ^ len;\n+\n+    while (len >= 8) {\n+        k  = data[0];\n+        k |= data[1] << 8;\n+        k |= data[2] << 16;\n+        k |= data[3] << 24;\n+        k |= (uint64_t)data[4] << 32;\n+        k |= (uint64_t)data[5] << 40;\n+        k |= (uint64_t)data[6] << 48;\n+        k |= (uint64_t)data[7] << 56;\n+\n+        k *= 0xc6a4a7935bd1e995ull;\n+        k ^= k >> 47;\n+        k *= 0xc6a4a7935bd1e995ull;\n+\n+        h ^= k;\n+        h *= 0xc6a4a7935bd1e995ull;\n+\n+        data += 8;\n+        len -= 8;\n+    }\n+\n+    switch (len) {\n+    case 7:\n+        h ^= (uint64_t)data[6] << 48;\n+        /* fall through */\n+    case 6:\n+        h ^= (uint64_t)data[5] << 40;\n+        /* fall through */\n+    case 5:\n+        h ^= (uint64_t)data[4] << 32;\n+        /* fall through */\n+    case 4:\n+        h ^= data[3] << 24;\n+        /* fall through */\n+    case 3:\n+        h ^= data[2] << 16;\n+        /* fall through */\n+    case 2:\n+        h ^= data[1] << 8;\n+        /* fall through */\n+    case 1:\n+        h ^= data[0];\n+        h *= 0xc6a4a7935bd1e995ull;\n+    }\n+\n+    h ^= h >> 47;\n+    h *= 0xc6a4a7935bd1e995ull;\n+    h ^= h >> 47;\n+\n+    return h;\n+}\ndiff -uNr a/src/core/ngx_murmurhash.h b/src/core/ngx_murmurhash.h\n--- a/src/core/ngx_murmurhash.h\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/core/ngx_murmurhash.h\t2019-10-17 22:51:22.605255498 +0800\n@@ -15,5 +15,7 @@\n \n uint32_t ngx_murmur_hash2(u_char *data, size_t len);\n \n+uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed);\n+\n \n #endif /* _NGX_MURMURHASH_H_INCLUDED_ */\ndiff -uNr a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c\n--- a/src/event/ngx_event_openssl.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/event/ngx_event_openssl.c\t2019-10-17 22:51:22.607255514 +0800\n@@ -1507,6 +1507,7 @@\n \n     sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);\n     sc->buffer_size = ssl->buffer_size;\n+    sc->dyn_rec = ssl->dyn_rec;\n \n     sc->session_ctx = ssl->ctx;\n \n@@ -2359,6 +2360,41 @@\n \n     for ( ;; ) {\n \n+        /* Dynamic record resizing:\n+           We want the initial records to fit into one TCP segment\n+           so we don't get TCP HoL blocking due to TCP Slow Start.\n+           A connection always starts with small records, but after\n+           a given amount of records sent, we make the records larger\n+           to reduce header overhead.\n+           After a connection has idled for a given timeout, begin\n+           the process from the start. The actual parameters are\n+           configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */\n+\n+        if (c->ssl->dyn_rec.timeout > 0 ) {\n+\n+            if (ngx_current_msec - c->ssl->dyn_rec_last_write >\n+                c->ssl->dyn_rec.timeout)\n+            {\n+                buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                c->ssl->dyn_rec_records_sent = 0;\n+\n+            } else {\n+                if (c->ssl->dyn_rec_records_sent >\n+                    c->ssl->dyn_rec.threshold * 2)\n+                {\n+                    buf->end = buf->start + c->ssl->buffer_size;\n+\n+                } else if (c->ssl->dyn_rec_records_sent >\n+                           c->ssl->dyn_rec.threshold)\n+                {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_hi;\n+\n+                } else {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                }\n+            }\n+        }\n+\n         while (in && buf->last < buf->end && send < limit) {\n             if (in->buf->last_buf || in->buf->flush) {\n                 flush = 1;\n@@ -2466,6 +2502,9 @@\n \n     if (n > 0) {\n \n+        c->ssl->dyn_rec_records_sent++;\n+        c->ssl->dyn_rec_last_write = ngx_current_msec;\n+\n         if (c->ssl->saved_read_handler) {\n \n             c->read->handler = c->ssl->saved_read_handler;\ndiff -uNr a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h\n--- a/src/event/ngx_event_openssl.h\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/event/ngx_event_openssl.h\t2019-10-17 22:51:22.607255514 +0800\n@@ -64,10 +64,19 @@\n #endif\n \n \n+typedef struct {\n+    ngx_msec_t                  timeout;\n+    ngx_uint_t                  threshold;\n+    size_t                      size_lo;\n+    size_t                      size_hi;\n+} ngx_ssl_dyn_rec_t;\n+\n+\n struct ngx_ssl_s {\n     SSL_CTX                    *ctx;\n     ngx_log_t                  *log;\n     size_t                      buffer_size;\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n };\n \n \n@@ -99,6 +108,10 @@\n     unsigned                    in_early:1;\n     unsigned                    early_preread:1;\n     unsigned                    write_blocked:1;\n+\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n+    ngx_msec_t                  dyn_rec_last_write;\n+    ngx_uint_t                  dyn_rec_records_sent;\n };\n \n \n@@ -108,7 +121,7 @@\n #define NGX_SSL_DFLT_BUILTIN_SCACHE  -5\n \n \n-#define NGX_SSL_MAX_SESSION_SIZE  4096\n+#define NGX_SSL_MAX_SESSION_SIZE  16384\n \n typedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;\n \ndiff -uNr a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c\n--- a/src/http/modules/ngx_http_ssl_module.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.c\t2019-10-17 22:51:22.608255522 +0800\n@@ -249,6 +249,41 @@\n       offsetof(ngx_http_ssl_srv_conf_t, early_data),\n       NULL },\n \n+    { ngx_string(\"ssl_dyn_rec_enable\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_flag_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_lo\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_hi\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_threshold\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold),\n+      NULL },\n+\n       ngx_null_command\n };\n \n@@ -371,10 +406,10 @@\n #if (NGX_DEBUG)\n     unsigned int            i;\n #endif\n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY)\n     ngx_http_connection_t  *hc;\n #endif\n-#if (NGX_HTTP_V2 || NGX_DEBUG)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY || NGX_DEBUG)\n     ngx_connection_t       *c;\n \n     c = ngx_ssl_get_connection(ssl_conn);\n@@ -388,9 +423,20 @@\n     }\n #endif\n \n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY)\n     hc = c->data;\n+#endif\n+\n+#if (NGX_HTTP_V2 && NGX_HTTP_SPDY)\n+    if (hc->addr_conf->http2 && hc->addr_conf->spdy) {\n+        srv = (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE\n+                                NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n+        srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE NGX_SPDY_NPN_ADVERTISE\n+                        NGX_HTTP_NPN_ADVERTISE) - 1;\n \n+    } else\n+#endif\n+#if (NGX_HTTP_V2)\n     if (hc->addr_conf->http2) {\n         srv =\n            (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n@@ -398,6 +444,13 @@\n \n     } else\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (hc->addr_conf->spdy) {\n+        srv = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n+        srvlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;\n+\n+    } else\n+#endif\n     {\n         srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;\n         srvlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;\n@@ -425,19 +478,32 @@\n ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,\n     const unsigned char **out, unsigned int *outlen, void *arg)\n {\n-#if (NGX_HTTP_V2 || NGX_DEBUG)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY || NGX_DEBUG)\n     ngx_connection_t  *c;\n \n     c = ngx_ssl_get_connection(ssl_conn);\n     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"SSL NPN advertised\");\n #endif\n \n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY)\n     {\n     ngx_http_connection_t  *hc;\n \n     hc = c->data;\n+#endif\n+\n+#if (NGX_HTTP_V2 && NGX_HTTP_SPDY)\n+    if (hc->addr_conf->http2 && hc->addr_conf->spdy) {\n+        *out = (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE\n+                                 NGX_SPDY_NPN_ADVERTISE\n+                                 NGX_HTTP_NPN_ADVERTISE;\n+        *outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE NGX_SPDY_NPN_ADVERTISE\n+                         NGX_HTTP_NPN_ADVERTISE) - 1;\n \n+        return SSL_TLSEXT_ERR_OK;\n+    } else\n+#endif\n+#if (NGX_HTTP_V2)\n     if (hc->addr_conf->http2) {\n         *out =\n             (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n@@ -445,6 +511,20 @@\n \n         return SSL_TLSEXT_ERR_OK;\n     }\n+#endif\n+#if (NGX_HTTP_V2 && NGX_HTTP_SPDY)\n+    else\n+#endif\n+#if (NGX_HTTP_SPDY)\n+    if (hc->addr_conf->spdy) {\n+        *out = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n+        *outlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;\n+\n+        return SSL_TLSEXT_ERR_OK;\n+    }\n+#endif\n+\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY)\n     }\n #endif\n \n@@ -580,6 +660,11 @@\n     sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;\n     sscf->stapling = NGX_CONF_UNSET;\n     sscf->stapling_verify = NGX_CONF_UNSET;\n+    sscf->dyn_rec_enable = NGX_CONF_UNSET;\n+    sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC;\n+    sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT;\n \n     return sscf;\n }\n@@ -647,6 +732,20 @@\n     ngx_conf_merge_str_value(conf->stapling_responder,\n                          prev->stapling_responder, \"\");\n \n+    ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0);\n+    ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout,\n+                             1000);\n+    /* Default sizes for the dynamic record sizes are defined to fit maximal\n+       TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi:\n+       1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo,\n+                             1369);\n+    /* 4229 = (1500 - 40 - 20 - 10) * 3  - 61 */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi,\n+                             4229);\n+    ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold,\n+                             40);\n+\n     conf->ssl.log = cf->log;\n \n     if (conf->enable) {\n@@ -857,6 +956,28 @@\n         return NGX_CONF_ERROR;\n     }\n \n+    if (conf->dyn_rec_enable) {\n+        conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout;\n+        conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold;\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_lo) {\n+            conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_lo = conf->buffer_size;\n+        }\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_hi) {\n+            conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_hi = conf->buffer_size;\n+        }\n+\n+    } else {\n+        conf->ssl.dyn_rec.timeout = 0;\n+    }\n+\n     return NGX_CONF_OK;\n }\n \ndiff -uNr a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h\n--- a/src/http/modules/ngx_http_ssl_module.h\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.h\t2019-10-17 22:51:22.609255531 +0800\n@@ -61,6 +61,12 @@\n \n     u_char                         *file;\n     ngx_uint_t                      line;\n+\n+    ngx_flag_t                      dyn_rec_enable;\n+    ngx_msec_t                      dyn_rec_timeout;\n+    size_t                          dyn_rec_size_lo;\n+    size_t                          dyn_rec_size_hi;\n+    ngx_uint_t                      dyn_rec_threshold;\n } ngx_http_ssl_srv_conf_t;\n \n \ndiff -uNr a/src/http/ngx_http.c b/src/http/ngx_http.c\n--- a/src/http/ngx_http.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/ngx_http.c\t2019-10-17 22:51:22.610255539 +0800\n@@ -1199,6 +1199,9 @@\n #if (NGX_HTTP_V2)\n     ngx_uint_t             http2;\n #endif\n+#if (NGX_HTTP_SPDY)\n+    ngx_uint_t             spdy;\n+#endif\n \n     /*\n      * we cannot compare whole sockaddr struct's as kernel\n@@ -1234,6 +1237,9 @@\n #if (NGX_HTTP_V2)\n         http2 = lsopt->http2 || addr[i].opt.http2;\n #endif\n+#if (NGX_HTTP_SPDY)\n+        spdy = lsopt->spdy || addr[i].opt.spdy;\n+#endif\n \n         if (lsopt->set) {\n \n@@ -1270,6 +1276,9 @@\n #if (NGX_HTTP_V2)\n         addr[i].opt.http2 = http2;\n #endif\n+#if (NGX_HTTP_SPDY)\n+        addr[i].opt.spdy = spdy;\n+#endif\n \n         return NGX_OK;\n     }\n@@ -1313,6 +1322,18 @@\n \n #endif\n \n+#if (NGX_HTTP_SPDY && NGX_HTTP_SSL                                            \\\n+     && !defined TLSEXT_TYPE_application_layer_protocol_negotiation           \\\n+     && !defined TLSEXT_TYPE_next_proto_neg)\n+    if (lsopt->spdy && lsopt->ssl) {\n+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n+                           \"nginx was built with OpenSSL that lacks ALPN \"\n+                           \"and NPN support, SPDY is not enabled for %s\",\n+                           lsopt->addr);\n+    }\n+\n+#endif\n+\n     addr = ngx_array_push(&port->addrs);\n     if (addr == NULL) {\n         return NGX_ERROR;\n@@ -1802,6 +1823,9 @@\n #if (NGX_HTTP_V2)\n         addrs[i].conf.http2 = addr[i].opt.http2;\n #endif\n+#if (NGX_HTTP_SPDY)\n+        addrs[i].conf.spdy = addr[i].opt.spdy;\n+#endif\n         addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n \n         if (addr[i].hash.buckets == NULL\n@@ -1867,6 +1891,9 @@\n #if (NGX_HTTP_V2)\n         addrs6[i].conf.http2 = addr[i].opt.http2;\n #endif\n+#if (NGX_HTTP_SPDY)\n+        addrs6[i].conf.spdy = addr[i].opt.spdy;\n+#endif\n         addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n \n         if (addr[i].hash.buckets == NULL\ndiff -uNr a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c\n--- a/src/http/ngx_http_core_module.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/ngx_http_core_module.c\t2019-10-17 22:51:22.612255555 +0800\n@@ -1938,6 +1938,13 @@\n         return NGX_DECLINED;\n     }\n \n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        r->gzip_ok = 1;\n+        return NGX_OK;\n+    }\n+#endif\n+\n     ae = r->headers_in.accept_encoding;\n     if (ae == NULL) {\n         return NGX_DECLINED;\n@@ -2295,6 +2302,9 @@\n #if (NGX_HTTP_V2)\n     sr->stream = r->stream;\n #endif\n+#if (NGX_HTTP_SPDY)\n+    sr->spdy_stream = r->spdy_stream;\n+#endif\n \n     sr->method = NGX_HTTP_GET;\n     sr->http_version = r->http_version;\n@@ -4000,11 +4010,15 @@\n         }\n \n         if (ngx_strcmp(value[n].data, \"spdy\") == 0) {\n-            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n-                               \"invalid parameter \\\"spdy\\\": \"\n-                               \"ngx_http_spdy_module was superseded \"\n-                               \"by ngx_http_v2_module\");\n+#if (NGX_HTTP_SPDY)\n+            lsopt.spdy = 1;\n             continue;\n+#else\n+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                               \"the \\\"spdy\\\" parameter requires \"\n+                               \"ngx_http_spdy_module\");\n+            return NGX_CONF_ERROR;\n+#endif\n         }\n \n         if (ngx_strncmp(value[n].data, \"so_keepalive=\", 13) == 0) {\ndiff -uNr a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h\n--- a/src/http/ngx_http_core_module.h\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/ngx_http_core_module.h\t2019-10-17 22:51:22.613255563 +0800\n@@ -75,6 +75,9 @@\n     unsigned                   wildcard:1;\n     unsigned                   ssl:1;\n     unsigned                   http2:1;\n+#if (NGX_HTTP_SPDY)\n+    unsigned                   spdy:1;\n+#endif\n #if (NGX_HAVE_INET6)\n     unsigned                   ipv6only:1;\n #endif\n@@ -237,6 +240,9 @@\n \n     unsigned                   ssl:1;\n     unsigned                   http2:1;\n+#if (NGX_HTTP_SPDY)\n+    unsigned                   spdy:1;\n+#endif\n     unsigned                   proxy_protocol:1;\n };\n \ndiff -uNr a/src/http/ngx_http.h b/src/http/ngx_http.h\n--- a/src/http/ngx_http.h\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/ngx_http.h\t2019-10-17 22:51:22.613255563 +0800\n@@ -19,6 +19,9 @@\n typedef struct ngx_http_file_cache_s  ngx_http_file_cache_t;\n typedef struct ngx_http_log_ctx_s     ngx_http_log_ctx_t;\n typedef struct ngx_http_chunked_s     ngx_http_chunked_t;\n+#if (NGX_HTTP_SPDY)\n+typedef struct ngx_http_spdy_stream_s  ngx_http_spdy_stream_t;\n+#endif\n typedef struct ngx_http_v2_stream_s   ngx_http_v2_stream_t;\n \n typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,\n@@ -35,9 +38,13 @@\n #include <ngx_http_upstream_round_robin.h>\n #include <ngx_http_core_module.h>\n \n+\n #if (NGX_HTTP_V2)\n #include <ngx_http_v2.h>\n #endif\n+#if (NGX_HTTP_SPDY)\n+#include <ngx_http_spdy.h>\n+#endif\n #if (NGX_HTTP_CACHE)\n #include <ngx_http_cache.h>\n #endif\ndiff -uNr a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c\n--- a/src/http/ngx_http_request_body.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/ngx_http_request_body.c\t2019-10-17 22:51:22.614255571 +0800\n@@ -84,6 +84,12 @@\n         goto done;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        rc = ngx_http_spdy_read_request_body(r, post_handler);\n+        goto done;\n+    }\n+#endif\n \n     preread = r->header_in->last - r->header_in->pos;\n \n@@ -524,6 +530,12 @@\n         return NGX_OK;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        r->spdy_stream->skip_data = 1;\n+        return NGX_OK;\n+    }\n+#endif\n \n     if (ngx_http_test_expect(r) != NGX_OK) {\n         return NGX_HTTP_INTERNAL_SERVER_ERROR;\ndiff -uNr a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c\n--- a/src/http/ngx_http_request.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/ngx_http_request.c\t2019-10-17 22:51:22.615255580 +0800\n@@ -324,6 +324,11 @@\n     rev->handler = ngx_http_wait_request_handler;\n     c->write->handler = ngx_http_empty_handler;\n \n+#if (NGX_HTTP_SPDY)\n+    if (hc->addr_conf->spdy) {\n+        rev->handler = ngx_http_spdy_init;\n+    }\n+#endif\n #if (NGX_HTTP_V2)\n     if (hc->addr_conf->http2) {\n         rev->handler = ngx_http_v2_init;\n@@ -830,6 +835,34 @@\n         }\n #endif\n \n+#if (NGX_HTTP_SPDY                                                            \\\n+     && (defined TLSEXT_TYPE_application_layer_protocol_negotiation           \\\n+         || defined TLSEXT_TYPE_next_proto_neg))\n+        {\n+        unsigned int             len;\n+        const unsigned char     *data;\n+        static const ngx_str_t   spdy = ngx_string(NGX_SPDY_NPN_NEGOTIATED);\n+\n+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n+        SSL_get0_alpn_selected(c->ssl->connection, &data, &len);\n+\n+#ifdef TLSEXT_TYPE_next_proto_neg\n+        if (len == 0) {\n+            SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);\n+        }\n+#endif\n+\n+#else /* TLSEXT_TYPE_next_proto_neg */\n+        SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);\n+#endif\n+\n+        if (len == spdy.len && ngx_strncmp(data, spdy.data, spdy.len) == 0) {\n+            ngx_http_spdy_init(c->read);\n+            return;\n+        }\n+        }\n+#endif\n+\n         c->log->action = \"waiting for request\";\n \n         c->read->handler = ngx_http_wait_request_handler;\n@@ -2686,6 +2719,12 @@\n         return;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        ngx_http_close_request(r, 0);\n+        return;\n+    }\n+#endif\n \n     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n \n@@ -2895,6 +2934,18 @@\n     }\n \n #endif\n+#if (NGX_HTTP_SPDY)\n+\n+    if (r->spdy_stream) {\n+        if (c->error) {\n+            err = 0;\n+            goto closed;\n+        }\n+\n+        return;\n+    }\n+\n+#endif\n \n #if (NGX_HAVE_KQUEUE)\n \n@@ -3562,6 +3613,12 @@\n         return;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        ngx_http_spdy_close_stream(r->spdy_stream, rc);\n+        return;\n+    }\n+#endif\n \n     ngx_http_free_request(r, rc);\n     ngx_http_close_connection(c);\ndiff -uNr a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h\n--- a/src/http/ngx_http_request.h\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/ngx_http_request.h\t2019-10-17 22:51:22.616255588 +0800\n@@ -432,6 +432,9 @@\n     int                              *captures;\n     u_char                           *captures_data;\n #endif\n+#if (NGX_HTTP_SPDY)\n+    ngx_http_spdy_stream_t           *spdy_stream;\n+#endif\n \n     size_t                            limit_rate;\n     size_t                            limit_rate_after;\ndiff -uNr a/src/http/ngx_http_spdy.c b/src/http/ngx_http_spdy.c\n--- a/src/http/ngx_http_spdy.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy.c\t2019-10-17 22:51:22.619255612 +0800\n@@ -0,0 +1,3701 @@\n+\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_spdy_module.h>\n+\n+#include <zlib.h>\n+\n+\n+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)\n+\n+#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \\\n+    *(uint32_t *) m == (c3 << 24 | c2 << 16 | c1 << 8 | c0)                   \\\n+        && m[4] == c4\n+\n+#else\n+\n+#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \\\n+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4\n+\n+#endif\n+\n+\n+#if (NGX_HAVE_NONALIGNED)\n+\n+#define ngx_spdy_frame_parse_uint16(p)  ntohs(*(uint16_t *) (p))\n+#define ngx_spdy_frame_parse_uint32(p)  ntohl(*(uint32_t *) (p))\n+\n+#else\n+\n+#define ngx_spdy_frame_parse_uint16(p) ((p)[0] << 8 | (p)[1])\n+#define ngx_spdy_frame_parse_uint32(p)                                        \\\n+    ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])\n+\n+#endif\n+\n+#define ngx_spdy_frame_parse_sid(p)                                           \\\n+    (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff)\n+#define ngx_spdy_frame_parse_delta(p)                                         \\\n+    (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff)\n+\n+\n+#define ngx_spdy_ctl_frame_check(h)                                           \\\n+    (((h) & 0xffff0000) == ngx_spdy_ctl_frame_head(0))\n+#define ngx_spdy_data_frame_check(h)                                          \\\n+    (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31))\n+\n+#define ngx_spdy_ctl_frame_type(h)   ((h) & 0x0000ffff)\n+#define ngx_spdy_frame_flags(p)      ((p) >> 24)\n+#define ngx_spdy_frame_length(p)     ((p) & 0x00ffffff)\n+#define ngx_spdy_frame_id(p)         ((p) & 0x00ffffff)\n+\n+\n+#define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE  4096\n+#define NGX_SPDY_CTL_FRAME_BUFFER_SIZE     16\n+\n+#define NGX_SPDY_PROTOCOL_ERROR            1\n+#define NGX_SPDY_INVALID_STREAM            2\n+#define NGX_SPDY_REFUSED_STREAM            3\n+#define NGX_SPDY_UNSUPPORTED_VERSION       4\n+#define NGX_SPDY_CANCEL                    5\n+#define NGX_SPDY_INTERNAL_ERROR            6\n+#define NGX_SPDY_FLOW_CONTROL_ERROR        7\n+#define NGX_SPDY_STREAM_IN_USE             8\n+#define NGX_SPDY_STREAM_ALREADY_CLOSED     9\n+/* deprecated                              10 */\n+#define NGX_SPDY_FRAME_TOO_LARGE           11\n+\n+#define NGX_SPDY_SETTINGS_MAX_STREAMS      4\n+#define NGX_SPDY_SETTINGS_INIT_WINDOW      7\n+\n+#define NGX_SPDY_SETTINGS_FLAG_PERSIST     0x01\n+#define NGX_SPDY_SETTINGS_FLAG_PERSISTED   0x02\n+\n+#define NGX_SPDY_MAX_WINDOW                NGX_MAX_INT32_VALUE\n+#define NGX_SPDY_CONNECTION_WINDOW         65536\n+#define NGX_SPDY_INIT_STREAM_WINDOW        65536\n+#define NGX_SPDY_STREAM_WINDOW             NGX_SPDY_MAX_WINDOW\n+\n+typedef struct {\n+    ngx_uint_t    hash;\n+    u_char        len;\n+    u_char        header[7];\n+    ngx_int_t   (*handler)(ngx_http_request_t *r);\n+} ngx_http_spdy_request_header_t;\n+\n+\n+static void ngx_http_spdy_read_handler(ngx_event_t *rev);\n+static void ngx_http_spdy_write_handler(ngx_event_t *wev);\n+static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc);\n+\n+static u_char *ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler);\n+\n+static u_char *ngx_http_spdy_state_inflate_error(\n+    ngx_http_spdy_connection_t *sc, int rc);\n+static u_char *ngx_http_spdy_state_protocol_error(\n+    ngx_http_spdy_connection_t *sc);\n+static u_char *ngx_http_spdy_state_internal_error(\n+    ngx_http_spdy_connection_t *sc);\n+\n+static ngx_int_t ngx_http_spdy_send_window_update(\n+    ngx_http_spdy_connection_t *sc, ngx_uint_t sid, ngx_uint_t delta);\n+static ngx_int_t ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc,\n+    ngx_uint_t sid, ngx_uint_t status, ngx_uint_t priority);\n+static ngx_int_t ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc);\n+static ngx_int_t ngx_http_spdy_settings_frame_handler(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);\n+static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctl_frame(\n+    ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority);\n+static ngx_int_t ngx_http_spdy_ctl_frame_handler(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);\n+\n+static ngx_http_spdy_stream_t *ngx_http_spdy_create_stream(\n+    ngx_http_spdy_connection_t *sc, ngx_uint_t id, ngx_uint_t priority);\n+static ngx_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id(\n+    ngx_http_spdy_connection_t *sc, ngx_uint_t sid);\n+#define ngx_http_spdy_streams_index_size(sscf)  (sscf->streams_index_mask + 1)\n+#define ngx_http_spdy_stream_index(sscf, sid)                                 \\\n+    ((sid >> 1) & sscf->streams_index_mask)\n+\n+static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r);\n+\n+static ngx_int_t ngx_http_spdy_handle_request_header(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_method(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_scheme(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_host(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_path(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_version(ngx_http_request_t *r);\n+\n+static ngx_int_t ngx_http_spdy_construct_request_line(ngx_http_request_t *r);\n+static void ngx_http_spdy_run_request(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r);\n+\n+static ngx_int_t ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream, ngx_uint_t status);\n+\n+static void ngx_http_spdy_close_stream_handler(ngx_event_t *ev);\n+\n+static void ngx_http_spdy_handle_connection_handler(ngx_event_t *rev);\n+static void ngx_http_spdy_keepalive_handler(ngx_event_t *rev);\n+static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,\n+    ngx_int_t rc);\n+\n+static ngx_int_t ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc,\n+    ssize_t delta);\n+\n+static void ngx_http_spdy_pool_cleanup(void *data);\n+\n+static void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size);\n+static void ngx_http_spdy_zfree(void *opaque, void *address);\n+\n+\n+static const u_char ngx_http_spdy_dict[] = {\n+    0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69,   /* - - - - o p t i */\n+    0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68,   /* o n s - - - - h */\n+    0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70,   /* e a d - - - - p */\n+    0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70,   /* o s t - - - - p */\n+    0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65,   /* u t - - - - d e */\n+    0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05,   /* l e t e - - - - */\n+    0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00,   /* t r a c e - - - */\n+    0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00,   /* - a c c e p t - */\n+    0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70,   /* - - - a c c e p */\n+    0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   /* t - c h a r s e */\n+    0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63,   /* t - - - - a c c */\n+    0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   /* e p t - e n c o */\n+    0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f,   /* d i n g - - - - */\n+    0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c,   /* a c c e p t - l */\n+    0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00,   /* a n g u a g e - */\n+    0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70,   /* - - - a c c e p */\n+    0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73,   /* t - r a n g e s */\n+    0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00,   /* - - - - a g e - */\n+    0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77,   /* - - - a l l o w */\n+    0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68,   /* - - - - a u t h */\n+    0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,   /* o r i z a t i o */\n+    0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63,   /* n - - - - c a c */\n+    0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72,   /* h e - c o n t r */\n+    0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f,   /* o l - - - - c o */\n+    0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,   /* n n e c t i o n */\n+    0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74,   /* - - - - c o n t */\n+    0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65,   /* e n t - b a s e */\n+    0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74,   /* - - - - c o n t */\n+    0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   /* e n t - e n c o */\n+    0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10,   /* d i n g - - - - */\n+    0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,   /* c o n t e n t - */\n+    0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,   /* l a n g u a g e */\n+    0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74,   /* - - - - c o n t */\n+    0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67,   /* e n t - l e n g */\n+    0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f,   /* t h - - - - c o */\n+    0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f,   /* n t e n t - l o */\n+    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,   /* c a t i o n - - */\n+    0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   /* - - c o n t e n */\n+    0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00,   /* t - m d 5 - - - */\n+    0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,   /* - c o n t e n t */\n+    0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00,   /* - r a n g e - - */\n+    0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   /* - - c o n t e n */\n+    0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00,   /* t - t y p e - - */\n+    0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00,   /* - - d a t e - - */\n+    0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00,   /* - - e t a g - - */\n+    0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,   /* - - e x p e c t */\n+    0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69,   /* - - - - e x p i */\n+    0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66,   /* r e s - - - - f */\n+    0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68,   /* r o m - - - - h */\n+    0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69,   /* o s t - - - - i */\n+    0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00,   /* f - m a t c h - */\n+    0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f,   /* - - - i f - m o */\n+    0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73,   /* d i f i e d - s */\n+    0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d,   /* i n c e - - - - */\n+    0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d,   /* i f - n o n e - */\n+    0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00,   /* m a t c h - - - */\n+    0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67,   /* - i f - r a n g */\n+    0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d,   /* e - - - - i f - */\n+    0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,   /* u n m o d i f i */\n+    0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65,   /* e d - s i n c e */\n+    0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74,   /* - - - - l a s t */\n+    0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,   /* - m o d i f i e */\n+    0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63,   /* d - - - - l o c */\n+    0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,   /* a t i o n - - - */\n+    0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72,   /* - m a x - f o r */\n+    0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00,   /* w a r d s - - - */\n+    0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00,   /* - p r a g m a - */\n+    0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79,   /* - - - p r o x y */\n+    0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,   /* - a u t h e n t */\n+    0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00,   /* i c a t e - - - */\n+    0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61,   /* - p r o x y - a */\n+    0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,   /* u t h o r i z a */\n+    0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05,   /* t i o n - - - - */\n+    0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00,   /* r a n g e - - - */\n+    0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72,   /* - r e f e r e r */\n+    0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72,   /* - - - - r e t r */\n+    0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00,   /* y - a f t e r - */\n+    0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,   /* - - - s e r v e */\n+    0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00,   /* r - - - - t e - */\n+    0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c,   /* - - - t r a i l */\n+    0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72,   /* e r - - - - t r */\n+    0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65,   /* a n s f e r - e */\n+    0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00,   /* n c o d i n g - */\n+    0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61,   /* - - - u p g r a */\n+    0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73,   /* d e - - - - u s */\n+    0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74,   /* e r - a g e n t */\n+    0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79,   /* - - - - v a r y */\n+    0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00,   /* - - - - v i a - */\n+    0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69,   /* - - - w a r n i */\n+    0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77,   /* n g - - - - w w */\n+    0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,   /* w - a u t h e n */\n+    0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00,   /* t i c a t e - - */\n+    0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,   /* - - m e t h o d */\n+    0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00,   /* - - - - g e t - */\n+    0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,   /* - - - s t a t u */\n+    0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30,   /* s - - - - 2 0 0 */\n+    0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76,   /* - O K - - - - v */\n+    0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00,   /* e r s i o n - - */\n+    0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,   /* - - H T T P - 1 */\n+    0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,   /* - 1 - - - - u r */\n+    0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62,   /* l - - - - p u b */\n+    0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73,   /* l i c - - - - s */\n+    0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69,   /* e t - c o o k i */\n+    0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65,   /* e - - - - k e e */\n+    0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00,   /* p - a l i v e - */\n+    0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69,   /* - - - o r i g i */\n+    0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32,   /* n 1 0 0 1 0 1 2 */\n+    0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35,   /* 0 1 2 0 2 2 0 5 */\n+    0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30,   /* 2 0 6 3 0 0 3 0 */\n+    0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33,   /* 2 3 0 3 3 0 4 3 */\n+    0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37,   /* 0 5 3 0 6 3 0 7 */\n+    0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30,   /* 4 0 2 4 0 5 4 0 */\n+    0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34,   /* 6 4 0 7 4 0 8 4 */\n+    0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31,   /* 0 9 4 1 0 4 1 1 */\n+    0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31,   /* 4 1 2 4 1 3 4 1 */\n+    0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34,   /* 4 4 1 5 4 1 6 4 */\n+    0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34,   /* 1 7 5 0 2 5 0 4 */\n+    0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e,   /* 5 0 5 2 0 3 - N */\n+    0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f,   /* o n - A u t h o */\n+    0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65,   /* r i t a t i v e */\n+    0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,   /* - I n f o r m a */\n+    0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20,   /* t i o n 2 0 4 - */\n+    0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65,   /* N o - C o n t e */\n+    0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f,   /* n t 3 0 1 - M o */\n+    0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d,   /* v e d - P e r m */\n+    0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34,   /* a n e n t l y 4 */\n+    0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52,   /* 0 0 - B a d - R */\n+    0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30,   /* e q u e s t 4 0 */\n+    0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68,   /* 1 - U n a u t h */\n+    0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30,   /* o r i z e d 4 0 */\n+    0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64,   /* 3 - F o r b i d */\n+    0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e,   /* d e n 4 0 4 - N */\n+    0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64,   /* o t - F o u n d */\n+    0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65,   /* 5 0 0 - I n t e */\n+    0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72,   /* r n a l - S e r */\n+    0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f,   /* v e r - E r r o */\n+    0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74,   /* r 5 0 1 - N o t */\n+    0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,   /* - I m p l e m e */\n+    0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20,   /* n t e d 5 0 3 - */\n+    0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20,   /* S e r v i c e - */\n+    0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,   /* U n a v a i l a */\n+    0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46,   /* b l e J a n - F */\n+    0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41,   /* e b - M a r - A */\n+    0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a,   /* p r - M a y - J */\n+    0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41,   /* u n - J u l - A */\n+    0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20,   /* u g - S e p t - */\n+    0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20,   /* O c t - N o v - */\n+    0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30,   /* D e c - 0 0 - 0 */\n+    0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e,   /* 0 - 0 0 - M o n */\n+    0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57,   /* - - T u e - - W */\n+    0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c,   /* e d - - T h u - */\n+    0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61,   /* - F r i - - S a */\n+    0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20,   /* t - - S u n - - */\n+    0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b,   /* G M T c h u n k */\n+    0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f,   /* e d - t e x t - */\n+    0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61,   /* h t m l - i m a */\n+    0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69,   /* g e - p n g - i */\n+    0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67,   /* m a g e - j p g */\n+    0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67,   /* - i m a g e - g */\n+    0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   /* i f - a p p l i */\n+    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   /* c a t i o n - x */\n+    0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   /* m l - a p p l i */\n+    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   /* c a t i o n - x */\n+    0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c,   /* h t m l - x m l */\n+    0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c,   /* - t e x t - p l */\n+    0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74,   /* a i n - t e x t */\n+    0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72,   /* - j a v a s c r */\n+    0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c,   /* i p t - p u b l */\n+    0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,   /* i c p r i v a t */\n+    0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65,   /* e m a x - a g e */\n+    0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65,   /* - g z i p - d e */\n+    0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64,   /* f l a t e - s d */\n+    0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   /* c h c h a r s e */\n+    0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63,   /* t - u t f - 8 c */\n+    0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69,   /* h a r s e t - i */\n+    0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d,   /* s o - 8 8 5 9 - */\n+    0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a,   /* 1 - u t f - - - */\n+    0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e          /* - e n q - 0 -   */\n+};\n+\n+\n+static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = {\n+    { 0, 6, \"method\", ngx_http_spdy_parse_method },\n+    { 0, 6, \"scheme\", ngx_http_spdy_parse_scheme },\n+    { 0, 4, \"host\", ngx_http_spdy_parse_host },\n+    { 0, 4, \"path\", ngx_http_spdy_parse_path },\n+    { 0, 7, \"version\", ngx_http_spdy_parse_version },\n+};\n+\n+#define NGX_SPDY_REQUEST_HEADERS                                              \\\n+    (sizeof(ngx_http_spdy_request_headers)                                    \\\n+     / sizeof(ngx_http_spdy_request_header_t))\n+\n+\n+void\n+ngx_http_spdy_init(ngx_event_t *rev)\n+{\n+    int                          rc;\n+    ngx_connection_t            *c;\n+    ngx_pool_cleanup_t          *cln;\n+    ngx_http_connection_t       *hc;\n+    ngx_http_spdy_srv_conf_t    *sscf;\n+    ngx_http_spdy_main_conf_t   *smcf;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    c = rev->data;\n+    hc = c->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"init spdy request\");\n+\n+    c->log->action = \"processing SPDY\";\n+\n+    smcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_spdy_module);\n+\n+    if (smcf->recv_buffer == NULL) {\n+        smcf->recv_buffer = ngx_palloc(ngx_cycle->pool, smcf->recv_buffer_size);\n+        if (smcf->recv_buffer == NULL) {\n+            ngx_http_close_connection(c);\n+            return;\n+        }\n+    }\n+\n+    sc = ngx_pcalloc(c->pool, sizeof(ngx_http_spdy_connection_t));\n+    if (sc == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->connection = c;\n+    sc->http_connection = hc;\n+\n+    sc->send_window = NGX_SPDY_CONNECTION_WINDOW;\n+    sc->recv_window = NGX_SPDY_CONNECTION_WINDOW;\n+\n+    sc->init_window = NGX_SPDY_INIT_STREAM_WINDOW;\n+\n+    sc->handler = hc->proxy_protocol ? ngx_http_spdy_proxy_protocol\n+                                     : ngx_http_spdy_state_head;\n+\n+    sc->zstream_in.zalloc = ngx_http_spdy_zalloc;\n+    sc->zstream_in.zfree = ngx_http_spdy_zfree;\n+    sc->zstream_in.opaque = sc;\n+\n+    rc = inflateInit(&sc->zstream_in);\n+    if (rc != Z_OK) {\n+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n+                      \"inflateInit() failed: %d\", rc);\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->zstream_out.zalloc = ngx_http_spdy_zalloc;\n+    sc->zstream_out.zfree = ngx_http_spdy_zfree;\n+    sc->zstream_out.opaque = sc;\n+\n+    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_spdy_module);\n+\n+    rc = deflateInit2(&sc->zstream_out, (int) sscf->headers_comp,\n+                      Z_DEFLATED, 11, 4, Z_DEFAULT_STRATEGY);\n+\n+    if (rc != Z_OK) {\n+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n+                      \"deflateInit2() failed: %d\", rc);\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    rc = deflateSetDictionary(&sc->zstream_out, ngx_http_spdy_dict,\n+                              sizeof(ngx_http_spdy_dict));\n+    if (rc != Z_OK) {\n+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n+                      \"deflateSetDictionary() failed: %d\", rc);\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);\n+    if (sc->pool == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t));\n+    if (cln == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    cln->handler = ngx_http_spdy_pool_cleanup;\n+    cln->data = sc;\n+\n+    sc->streams_index = ngx_pcalloc(sc->pool,\n+                                    ngx_http_spdy_streams_index_size(sscf)\n+                                    * sizeof(ngx_http_spdy_stream_t *));\n+    if (sc->streams_index == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    if (ngx_http_spdy_send_settings(sc) == NGX_ERROR) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    if (ngx_http_spdy_send_window_update(sc, 0, NGX_SPDY_MAX_WINDOW\n+                                                - sc->recv_window)\n+        == NGX_ERROR)\n+    {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->recv_window = NGX_SPDY_MAX_WINDOW;\n+\n+    ngx_queue_init(&sc->waiting);\n+    ngx_queue_init(&sc->posted);\n+\n+    c->data = sc;\n+\n+    rev->handler = ngx_http_spdy_read_handler;\n+    c->write->handler = ngx_http_spdy_write_handler;\n+\n+    ngx_http_spdy_read_handler(rev);\n+}\n+\n+\n+static void\n+ngx_http_spdy_read_handler(ngx_event_t *rev)\n+{\n+    u_char                      *p, *end;\n+    size_t                       available;\n+    ssize_t                      n;\n+    ngx_connection_t            *c;\n+    ngx_http_spdy_main_conf_t   *smcf;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    c = rev->data;\n+    sc = c->data;\n+\n+    if (rev->timedout) {\n+        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_REQUEST_TIME_OUT);\n+        return;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"spdy read handler\");\n+\n+    sc->blocked = 1;\n+\n+    smcf = ngx_http_get_module_main_conf(sc->http_connection->conf_ctx,\n+                                         ngx_http_spdy_module);\n+\n+    available = smcf->recv_buffer_size - 2 * NGX_SPDY_STATE_BUFFER_SIZE;\n+\n+    do {\n+        p = smcf->recv_buffer;\n+\n+        ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE);\n+        end = p + sc->buffer_used;\n+\n+        n = c->recv(c, end, available);\n+\n+        if (n == NGX_AGAIN) {\n+            break;\n+        }\n+\n+        if (n == 0 && (sc->incomplete || sc->processing)) {\n+            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n+                          \"client prematurely closed connection\");\n+        }\n+\n+        if (n == 0 || n == NGX_ERROR) {\n+            ngx_http_spdy_finalize_connection(sc,\n+                                              NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+            return;\n+        }\n+\n+        end += n;\n+\n+        sc->buffer_used = 0;\n+        sc->incomplete = 0;\n+\n+        do {\n+            p = sc->handler(sc, p, end);\n+\n+            if (p == NULL) {\n+                return;\n+            }\n+\n+        } while (p != end);\n+\n+    } while (rev->ready);\n+\n+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return;\n+    }\n+\n+    if (sc->last_out && ngx_http_spdy_send_output_queue(sc) == NGX_ERROR) {\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+        return;\n+    }\n+\n+    sc->blocked = 0;\n+\n+    if (sc->processing) {\n+        if (rev->timer_set) {\n+            ngx_del_timer(rev);\n+        }\n+        return;\n+    }\n+\n+    ngx_http_spdy_handle_connection(sc);\n+}\n+\n+\n+static void\n+ngx_http_spdy_write_handler(ngx_event_t *wev)\n+{\n+    ngx_int_t                    rc;\n+    ngx_queue_t                 *q;\n+    ngx_connection_t            *c;\n+    ngx_http_spdy_stream_t      *stream;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    c = wev->data;\n+    sc = c->data;\n+\n+    if (wev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"spdy write event timed out\");\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+        return;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"spdy write handler\");\n+\n+    sc->blocked = 1;\n+\n+    rc = ngx_http_spdy_send_output_queue(sc);\n+\n+    if (rc == NGX_ERROR) {\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+        return;\n+    }\n+\n+    while (!ngx_queue_empty(&sc->posted)) {\n+        q = ngx_queue_head(&sc->posted);\n+\n+        ngx_queue_remove(q);\n+\n+        stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);\n+\n+        stream->handled = 0;\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"run spdy stream %ui\", stream->id);\n+\n+        wev = stream->request->connection->write;\n+        wev->handler(wev);\n+    }\n+\n+    sc->blocked = 0;\n+\n+    if (rc == NGX_AGAIN) {\n+        return;\n+    }\n+\n+    ngx_http_spdy_handle_connection(sc);\n+}\n+\n+\n+ngx_int_t\n+ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc)\n+{\n+    int                         tcp_nodelay;\n+    ngx_chain_t                *cl;\n+    ngx_event_t                *wev;\n+    ngx_connection_t           *c;\n+    ngx_http_core_loc_conf_t   *clcf;\n+    ngx_http_spdy_out_frame_t  *out, *frame, *fn;\n+\n+    c = sc->connection;\n+\n+    if (c->error) {\n+        return NGX_ERROR;\n+    }\n+\n+    wev = c->write;\n+\n+    if (!wev->ready) {\n+        return NGX_OK;\n+    }\n+\n+    cl = NULL;\n+    out = NULL;\n+\n+    for (frame = sc->last_out; frame; frame = fn) {\n+        frame->last->next = cl;\n+        cl = frame->first;\n+\n+        fn = frame->next;\n+        frame->next = out;\n+        out = frame;\n+\n+        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"spdy frame out: %p sid:%ui prio:%ui bl:%d len:%uz\",\n+                       out, out->stream ? out->stream->id : 0, out->priority,\n+                       out->blocked, out->length);\n+    }\n+\n+    cl = c->send_chain(c, cl, 0);\n+\n+    if (cl == NGX_CHAIN_ERROR) {\n+        goto error;\n+    }\n+\n+    clcf = ngx_http_get_module_loc_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_core_module);\n+\n+    if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {\n+        goto error;\n+    }\n+\n+    if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {\n+        if (ngx_tcp_push(c->fd) == -1) {\n+            ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n \" failed\");\n+            goto error;\n+        }\n+\n+        c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;\n+        tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;\n+\n+    } else {\n+        tcp_nodelay = 1;\n+    }\n+\n+    if (tcp_nodelay\n+        && clcf->tcp_nodelay\n+        && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET)\n+    {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"tcp_nodelay\");\n+\n+        if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,\n+                       (const void *) &tcp_nodelay, sizeof(int))\n+            == -1)\n+        {\n+#if (NGX_SOLARIS)\n+            /* Solaris returns EINVAL if a socket has been shut down */\n+            c->log_error = NGX_ERROR_IGNORE_EINVAL;\n+#endif\n+\n+            ngx_connection_error(c, ngx_socket_errno,\n+                                 \"setsockopt(TCP_NODELAY) failed\");\n+\n+            c->log_error = NGX_ERROR_INFO;\n+            goto error;\n+        }\n+\n+        c->tcp_nodelay = NGX_TCP_NODELAY_SET;\n+    }\n+\n+    if (cl) {\n+        ngx_add_timer(wev, clcf->send_timeout);\n+\n+    } else {\n+        if (wev->timer_set) {\n+            ngx_del_timer(wev);\n+        }\n+    }\n+\n+    for ( /* void */ ; out; out = fn) {\n+        fn = out->next;\n+\n+        if (out->handler(sc, out) != NGX_OK) {\n+            out->blocked = 1;\n+            out->priority = NGX_SPDY_HIGHEST_PRIORITY;\n+            break;\n+        }\n+\n+        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"spdy frame sent: %p sid:%ui bl:%d len:%uz\",\n+                       out, out->stream ? out->stream->id : 0,\n+                       out->blocked, out->length);\n+    }\n+\n+    frame = NULL;\n+\n+    for ( /* void */ ; out; out = fn) {\n+        fn = out->next;\n+        out->next = frame;\n+        frame = out;\n+    }\n+\n+    sc->last_out = frame;\n+\n+    return NGX_OK;\n+\n+error:\n+\n+    c->error = 1;\n+\n+    if (!sc->blocked) {\n+        ngx_post_event(wev, &ngx_posted_events);\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+\n+static void\n+ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc)\n+{\n+    ngx_connection_t          *c;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    if (sc->last_out || sc->processing) {\n+        return;\n+    }\n+\n+    c = sc->connection;\n+\n+    if (c->error) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    if (c->buffered) {\n+        return;\n+    }\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+    if (sc->incomplete) {\n+        ngx_add_timer(c->read, sscf->recv_timeout);\n+        return;\n+    }\n+\n+    if (ngx_terminate || ngx_exiting) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    ngx_destroy_pool(sc->pool);\n+\n+    sc->pool = NULL;\n+    sc->free_ctl_frames = NULL;\n+    sc->free_fake_connections = NULL;\n+\n+#if (NGX_HTTP_SSL)\n+    if (c->ssl) {\n+        ngx_ssl_free_buffer(c);\n+    }\n+#endif\n+\n+    c->destroyed = 1;\n+    c->idle = 1;\n+    ngx_reusable_connection(c, 1);\n+\n+    c->write->handler = ngx_http_empty_handler;\n+    c->read->handler = ngx_http_spdy_keepalive_handler;\n+\n+    if (c->write->timer_set) {\n+        ngx_del_timer(c->write);\n+    }\n+\n+    ngx_add_timer(c->read, sscf->keepalive_timeout);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_log_t  *log;\n+\n+    log = sc->connection->log;\n+    log->action = \"reading PROXY protocol\";\n+\n+    pos = ngx_proxy_protocol_read(sc->connection, pos, end);\n+\n+    log->action = \"processing SPDY\";\n+\n+    if (pos == NULL) {\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    uint32_t    head, flen;\n+    ngx_uint_t  type;\n+\n+    if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_head);\n+    }\n+\n+    head = ngx_spdy_frame_parse_uint32(pos);\n+\n+    pos += sizeof(uint32_t);\n+\n+    flen = ngx_spdy_frame_parse_uint32(pos);\n+\n+    sc->flags = ngx_spdy_frame_flags(flen);\n+    sc->length = ngx_spdy_frame_length(flen);\n+\n+    pos += sizeof(uint32_t);\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"process spdy frame head:%08XD f:%Xd l:%uz\",\n+                   head, sc->flags, sc->length);\n+\n+    if (ngx_spdy_ctl_frame_check(head)) {\n+        type = ngx_spdy_ctl_frame_type(head);\n+\n+        switch (type) {\n+\n+        case NGX_SPDY_SYN_STREAM:\n+            return ngx_http_spdy_state_syn_stream(sc, pos, end);\n+\n+        case NGX_SPDY_SYN_REPLY:\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client sent unexpected SYN_REPLY frame\");\n+            return ngx_http_spdy_state_protocol_error(sc);\n+\n+        case NGX_SPDY_RST_STREAM:\n+            return ngx_http_spdy_state_rst_stream(sc, pos, end);\n+\n+        case NGX_SPDY_SETTINGS:\n+            return ngx_http_spdy_state_settings(sc, pos, end);\n+\n+        case NGX_SPDY_PING:\n+            return ngx_http_spdy_state_ping(sc, pos, end);\n+\n+        case NGX_SPDY_GOAWAY:\n+            return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */\n+\n+        case NGX_SPDY_HEADERS:\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client sent unexpected HEADERS frame\");\n+            return ngx_http_spdy_state_protocol_error(sc);\n+\n+        case NGX_SPDY_WINDOW_UPDATE:\n+            return ngx_http_spdy_state_window_update(sc, pos, end);\n+\n+        default:\n+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"spdy control frame with unknown type %ui\", type);\n+            return ngx_http_spdy_state_skip(sc, pos, end);\n+        }\n+    }\n+\n+    if (ngx_spdy_data_frame_check(head)) {\n+        sc->stream = ngx_http_spdy_get_stream_by_id(sc, head);\n+        return ngx_http_spdy_state_data(sc, pos, end);\n+    }\n+\n+    ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                  \"client sent invalid frame\");\n+\n+    return ngx_http_spdy_state_protocol_error(sc);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_uint_t                 sid, prio;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    if (end - pos < NGX_SPDY_SYN_STREAM_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_syn_stream);\n+    }\n+\n+    if (sc->length <= NGX_SPDY_SYN_STREAM_SIZE) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent SYN_STREAM frame with incorrect length %uz\",\n+                      sc->length);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sc->length -= NGX_SPDY_SYN_STREAM_SIZE;\n+\n+    sid = ngx_spdy_frame_parse_sid(pos);\n+    prio = pos[8] >> 5;\n+\n+    pos += NGX_SPDY_SYN_STREAM_SIZE;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy SYN_STREAM frame sid:%ui prio:%ui\", sid, prio);\n+\n+    if (sid % 2 == 0 || sid <= sc->last_sid) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent SYN_STREAM frame \"\n+                      \"with invalid Stream-ID %ui\", sid);\n+\n+        stream = ngx_http_spdy_get_stream_by_id(sc, sid);\n+\n+        if (stream) {\n+            if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                               NGX_SPDY_PROTOCOL_ERROR)\n+                != NGX_OK)\n+            {\n+                return ngx_http_spdy_state_internal_error(sc);\n+            }\n+        }\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sc->last_sid = sid;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    if (sc->processing >= sscf->concurrent_streams) {\n+\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"concurrent streams exceeded %ui\", sc->processing);\n+\n+        if (ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM,\n+                                          prio)\n+            != NGX_OK)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+    }\n+\n+    stream = ngx_http_spdy_create_stream(sc, sid, prio);\n+    if (stream == NULL) {\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    stream->in_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0;\n+\n+    stream->request->request_length = NGX_SPDY_FRAME_HEADER_SIZE\n+                                      + NGX_SPDY_SYN_STREAM_SIZE\n+                                      + sc->length;\n+\n+    sc->stream = stream;\n+\n+    return ngx_http_spdy_state_headers(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    int                  z;\n+    size_t               size;\n+    ngx_buf_t           *buf;\n+    ngx_int_t            rc;\n+    ngx_http_request_t  *r;\n+\n+    size = end - pos;\n+\n+    if (size == 0) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_headers);\n+    }\n+\n+    if (size > sc->length) {\n+        size = sc->length;\n+    }\n+\n+    r = sc->stream->request;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"process spdy header block %uz of %uz\", size, sc->length);\n+\n+    buf = r->header_in;\n+\n+    sc->zstream_in.next_in = pos;\n+    sc->zstream_in.avail_in = size;\n+    sc->zstream_in.next_out = buf->last;\n+\n+    /* one byte is reserved for null-termination of the last header value */\n+    sc->zstream_in.avail_out = buf->end - buf->last - 1;\n+\n+    z = inflate(&sc->zstream_in, Z_NO_FLUSH);\n+\n+    if (z == Z_NEED_DICT) {\n+        z = inflateSetDictionary(&sc->zstream_in, ngx_http_spdy_dict,\n+                                 sizeof(ngx_http_spdy_dict));\n+\n+        if (z != Z_OK) {\n+            if (z == Z_DATA_ERROR) {\n+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                              \"client sent SYN_STREAM frame with header \"\n+                              \"block encoded using wrong dictionary: %ul\",\n+                              (u_long) sc->zstream_in.adler);\n+\n+                return ngx_http_spdy_state_protocol_error(sc);\n+            }\n+\n+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n+                          \"inflateSetDictionary() failed: %d\", z);\n+\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"spdy inflateSetDictionary(): %d\", z);\n+\n+        z = sc->zstream_in.avail_in ? inflate(&sc->zstream_in, Z_NO_FLUSH)\n+                                    : Z_OK;\n+    }\n+\n+    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n+                   sc->zstream_in.next_in, sc->zstream_in.next_out,\n+                   sc->zstream_in.avail_in, sc->zstream_in.avail_out,\n+                   z);\n+\n+    if (z != Z_OK) {\n+        return ngx_http_spdy_state_inflate_error(sc, z);\n+    }\n+\n+    sc->length -= sc->zstream_in.next_in - pos;\n+    pos = sc->zstream_in.next_in;\n+\n+    buf->last = sc->zstream_in.next_out;\n+\n+    if (r->headers_in.headers.part.elts == NULL) {\n+\n+        if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) {\n+\n+            if (sc->length == 0) {\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                               \"premature end of spdy header block\");\n+\n+                return ngx_http_spdy_state_headers_error(sc, pos, end);\n+            }\n+\n+            return ngx_http_spdy_state_save(sc, pos, end,\n+                                            ngx_http_spdy_state_headers);\n+        }\n+\n+        sc->entries = ngx_spdy_frame_parse_uint32(buf->pos);\n+\n+        buf->pos += NGX_SPDY_NV_NUM_SIZE;\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"spdy header block has %ui entries\",\n+                       sc->entries);\n+\n+        if (ngx_list_init(&r->headers_in.headers, r->pool, 20,\n+                          sizeof(ngx_table_elt_t))\n+            != NGX_OK)\n+        {\n+            ngx_http_spdy_close_stream(sc->stream,\n+                                       NGX_HTTP_INTERNAL_SERVER_ERROR);\n+            return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+        }\n+\n+        if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,\n+                           sizeof(ngx_table_elt_t *))\n+            != NGX_OK)\n+        {\n+            ngx_http_spdy_close_stream(sc->stream,\n+                                       NGX_HTTP_INTERNAL_SERVER_ERROR);\n+            return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+        }\n+    }\n+\n+    while (sc->entries) {\n+\n+        rc = ngx_http_spdy_parse_header(r);\n+\n+        switch (rc) {\n+\n+        case NGX_DONE:\n+            sc->entries--;\n+\n+        case NGX_OK:\n+            break;\n+\n+        case NGX_AGAIN:\n+\n+            if (sc->zstream_in.avail_in) {\n+\n+                rc = ngx_http_spdy_alloc_large_header_buffer(r);\n+\n+                if (rc == NGX_DECLINED) {\n+                    ngx_http_finalize_request(r,\n+                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);\n+                    return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+                }\n+\n+                if (rc != NGX_OK) {\n+                    ngx_http_spdy_close_stream(sc->stream,\n+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n+                    return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+                }\n+\n+                /* null-terminate the last processed header name or value */\n+                *buf->pos = '\\0';\n+\n+                buf = r->header_in;\n+\n+                sc->zstream_in.next_out = buf->last;\n+\n+                /* one byte is reserved for null-termination */\n+                sc->zstream_in.avail_out = buf->end - buf->last - 1;\n+\n+                z = inflate(&sc->zstream_in, Z_NO_FLUSH);\n+\n+                ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                           \"spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n+                           sc->zstream_in.next_in, sc->zstream_in.next_out,\n+                           sc->zstream_in.avail_in, sc->zstream_in.avail_out,\n+                           z);\n+\n+                if (z != Z_OK) {\n+                    return ngx_http_spdy_state_inflate_error(sc, z);\n+                }\n+\n+                sc->length -= sc->zstream_in.next_in - pos;\n+                pos = sc->zstream_in.next_in;\n+\n+                buf->last = sc->zstream_in.next_out;\n+\n+                continue;\n+            }\n+\n+            if (sc->length == 0) {\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                               \"premature end of spdy header block\");\n+\n+                return ngx_http_spdy_state_headers_error(sc, pos, end);\n+            }\n+\n+            return ngx_http_spdy_state_save(sc, pos, end,\n+                                            ngx_http_spdy_state_headers);\n+\n+        case NGX_HTTP_PARSE_INVALID_HEADER:\n+            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+            return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+\n+        default: /* NGX_ERROR */\n+            return ngx_http_spdy_state_headers_error(sc, pos, end);\n+        }\n+\n+        /* a header line has been parsed successfully */\n+\n+        rc = ngx_http_spdy_handle_request_header(r);\n+\n+        if (rc != NGX_OK) {\n+            if (rc == NGX_HTTP_PARSE_INVALID_HEADER) {\n+                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+                return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+            }\n+\n+            if (rc != NGX_ABORT) {\n+                ngx_http_spdy_close_stream(sc->stream,\n+                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n+            }\n+\n+            return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+        }\n+    }\n+\n+    if (buf->pos != buf->last || sc->zstream_in.avail_in) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"incorrect number of spdy header block entries\");\n+\n+        return ngx_http_spdy_state_headers_error(sc, pos, end);\n+    }\n+\n+    if (sc->length) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_headers);\n+    }\n+\n+    /* null-terminate the last header value */\n+    *buf->pos = '\\0';\n+\n+    ngx_http_spdy_run_request(r);\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    int     n;\n+    size_t  size;\n+    u_char  buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE];\n+\n+    if (sc->length == 0) {\n+        return ngx_http_spdy_state_complete(sc, pos, end);\n+    }\n+\n+    size = end - pos;\n+\n+    if (size == 0) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_headers_skip);\n+    }\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy header block skip %uz of %uz\", size, sc->length);\n+\n+    sc->zstream_in.next_in = pos;\n+    sc->zstream_in.avail_in = (size < sc->length) ? size : sc->length;\n+\n+    while (sc->zstream_in.avail_in) {\n+        sc->zstream_in.next_out = buffer;\n+        sc->zstream_in.avail_out = NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE;\n+\n+        n = inflate(&sc->zstream_in, Z_NO_FLUSH);\n+\n+        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n+                       sc->zstream_in.next_in, sc->zstream_in.next_out,\n+                       sc->zstream_in.avail_in, sc->zstream_in.avail_out,\n+                       n);\n+\n+        if (n != Z_OK) {\n+            return ngx_http_spdy_state_inflate_error(sc, n);\n+        }\n+    }\n+\n+    pos = sc->zstream_in.next_in;\n+\n+    if (size < sc->length) {\n+        sc->length -= size;\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_headers_skip);\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    stream = sc->stream;\n+\n+    ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                  \"client sent SYN_STREAM frame for stream %ui \"\n+                  \"with invalid header block\", stream->id);\n+\n+    if (ngx_http_spdy_send_rst_stream(sc, stream->id, NGX_SPDY_PROTOCOL_ERROR,\n+                                      stream->priority)\n+        != NGX_OK)\n+    {\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    stream->out_closed = 1;\n+\n+    ngx_http_spdy_close_stream(stream, NGX_HTTP_BAD_REQUEST);\n+\n+    return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    size_t                   delta;\n+    ngx_uint_t               sid;\n+    ngx_event_t             *wev;\n+    ngx_queue_t             *q;\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    if (end - pos < NGX_SPDY_WINDOW_UPDATE_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_window_update);\n+    }\n+\n+    if (sc->length != NGX_SPDY_WINDOW_UPDATE_SIZE) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent WINDOW_UPDATE frame \"\n+                      \"with incorrect length %uz\", sc->length);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sid = ngx_spdy_frame_parse_sid(pos);\n+\n+    pos += NGX_SPDY_SID_SIZE;\n+\n+    delta = ngx_spdy_frame_parse_delta(pos);\n+\n+    pos += NGX_SPDY_DELTA_SIZE;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy WINDOW_UPDATE sid:%ui delta:%uz\", sid, delta);\n+\n+    if (sid) {\n+        stream = ngx_http_spdy_get_stream_by_id(sc, sid);\n+\n+        if (stream == NULL) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"unknown spdy stream\");\n+\n+            return ngx_http_spdy_state_complete(sc, pos, end);\n+        }\n+\n+        if (stream->send_window > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta)) {\n+\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client violated flow control for stream %ui: \"\n+                          \"received WINDOW_UPDATE frame with delta %uz \"\n+                          \"not allowed for window %z\",\n+                          sid, delta, stream->send_window);\n+\n+            if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                               NGX_SPDY_FLOW_CONTROL_ERROR)\n+                == NGX_ERROR)\n+            {\n+                return ngx_http_spdy_state_internal_error(sc);\n+            }\n+\n+            return ngx_http_spdy_state_complete(sc, pos, end);\n+        }\n+\n+        stream->send_window += delta;\n+\n+        if (stream->exhausted) {\n+            stream->exhausted = 0;\n+\n+            wev = stream->request->connection->write;\n+\n+            if (!wev->timer_set) {\n+                wev->delayed = 0;\n+                wev->handler(wev);\n+            }\n+        }\n+\n+    } else {\n+        sc->send_window += delta;\n+\n+        if (sc->send_window > NGX_SPDY_MAX_WINDOW) {\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client violated connection flow control: \"\n+                          \"received WINDOW_UPDATE frame with delta %uz \"\n+                          \"not allowed for window %uz\",\n+                          delta, sc->send_window);\n+\n+            return ngx_http_spdy_state_protocol_error(sc);\n+        }\n+\n+        while (!ngx_queue_empty(&sc->waiting)) {\n+            q = ngx_queue_head(&sc->waiting);\n+\n+            ngx_queue_remove(q);\n+\n+            stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);\n+\n+            stream->handled = 0;\n+\n+            wev = stream->request->connection->write;\n+\n+            if (!wev->timer_set) {\n+                wev->delayed = 0;\n+                wev->handler(wev);\n+\n+                if (sc->send_window == 0) {\n+                    break;\n+                }\n+            }\n+        }\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy DATA frame\");\n+\n+    if (sc->length > sc->recv_window) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client violated connection flow control: \"\n+                      \"received DATA frame length %uz, available window %uz\",\n+                      sc->length, sc->recv_window);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sc->recv_window -= sc->length;\n+\n+    if (sc->recv_window < NGX_SPDY_MAX_WINDOW / 4) {\n+\n+        if (ngx_http_spdy_send_window_update(sc, 0,\n+                                             NGX_SPDY_MAX_WINDOW\n+                                             - sc->recv_window)\n+            == NGX_ERROR)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        sc->recv_window = NGX_SPDY_MAX_WINDOW;\n+    }\n+\n+    stream = sc->stream;\n+\n+    if (stream == NULL) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"unknown spdy stream\");\n+\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    if (sc->length > stream->recv_window) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client violated flow control for stream %ui: \"\n+                      \"received DATA frame length %uz, available window %uz\",\n+                      stream->id, sc->length, stream->recv_window);\n+\n+        if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                           NGX_SPDY_FLOW_CONTROL_ERROR)\n+            == NGX_ERROR)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    stream->recv_window -= sc->length;\n+\n+    if (stream->recv_window < NGX_SPDY_STREAM_WINDOW / 4) {\n+\n+        if (ngx_http_spdy_send_window_update(sc, stream->id,\n+                                             NGX_SPDY_STREAM_WINDOW\n+                                             - stream->recv_window)\n+            == NGX_ERROR)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        stream->recv_window = NGX_SPDY_STREAM_WINDOW;\n+    }\n+\n+    if (stream->in_closed) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent DATA frame for half-closed stream %ui\",\n+                      stream->id);\n+\n+        if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                           NGX_SPDY_STREAM_ALREADY_CLOSED)\n+            == NGX_ERROR)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    return ngx_http_spdy_state_read_data(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    size_t                     size;\n+    ssize_t                    n;\n+    ngx_buf_t                 *buf;\n+    ngx_int_t                  rc;\n+    ngx_temp_file_t           *tf;\n+    ngx_http_request_t        *r;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_request_body_t   *rb;\n+    ngx_http_core_loc_conf_t  *clcf;\n+\n+    stream = sc->stream;\n+\n+    if (stream == NULL) {\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    if (stream->skip_data) {\n+\n+        if (sc->flags & NGX_SPDY_FLAG_FIN) {\n+            stream->in_closed = 1;\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"skipping spdy DATA frame, reason: %d\",\n+                       stream->skip_data);\n+\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    size = end - pos;\n+\n+    if (size > sc->length) {\n+        size = sc->length;\n+    }\n+\n+    r = stream->request;\n+\n+    if (r->request_body == NULL\n+        && ngx_http_spdy_init_request_body(r) != NGX_OK)\n+    {\n+        stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    rb = r->request_body;\n+    tf = rb->temp_file;\n+    buf = rb->buf;\n+\n+    if (size) {\n+        rb->rest += size;\n+\n+        if (r->headers_in.content_length_n != -1\n+            && r->headers_in.content_length_n < rb->rest)\n+        {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client intended to send body data \"\n+                          \"larger than declared\");\n+\n+            stream->skip_data = NGX_SPDY_DATA_ERROR;\n+            goto error;\n+\n+        } else {\n+            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+            if (clcf->client_max_body_size\n+                && clcf->client_max_body_size < rb->rest)\n+            {\n+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n+                              \"client intended to send \"\n+                              \"too large chunked body: %O bytes\", rb->rest);\n+\n+                stream->skip_data = NGX_SPDY_DATA_ERROR;\n+                goto error;\n+            }\n+        }\n+\n+        sc->length -= size;\n+\n+        if (tf) {\n+            buf->start = pos;\n+            buf->pos = pos;\n+\n+            pos += size;\n+\n+            buf->end = pos;\n+            buf->last = pos;\n+\n+            n = ngx_write_chain_to_temp_file(tf, rb->bufs);\n+\n+            /* TODO: n == 0 or not complete and level event */\n+\n+            if (n == NGX_ERROR) {\n+                stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;\n+                goto error;\n+            }\n+\n+            tf->offset += n;\n+\n+        } else {\n+            buf->last = ngx_cpymem(buf->last, pos, size);\n+            pos += size;\n+        }\n+\n+        r->request_length += size;\n+    }\n+\n+    if (sc->length) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_read_data);\n+    }\n+\n+    if (sc->flags & NGX_SPDY_FLAG_FIN) {\n+\n+        stream->in_closed = 1;\n+\n+        if (r->headers_in.content_length_n < 0) {\n+            r->headers_in.content_length_n = rb->rest;\n+\n+        } else if (r->headers_in.content_length_n != rb->rest) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client prematurely closed stream: \"\n+                          \"only %O out of %O bytes of request body received\",\n+                          rb->rest, r->headers_in.content_length_n);\n+\n+            stream->skip_data = NGX_SPDY_DATA_ERROR;\n+            goto error;\n+        }\n+\n+        if (tf) {\n+            ngx_memzero(buf, sizeof(ngx_buf_t));\n+\n+            buf->in_file = 1;\n+            buf->file_last = tf->file.offset;\n+            buf->file = &tf->file;\n+\n+            rb->buf = NULL;\n+        }\n+\n+        if (rb->post_handler) {\n+            r->read_event_handler = ngx_http_block_reading;\n+            rb->post_handler(r);\n+        }\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+\n+error:\n+\n+    if (rb->post_handler) {\n+\n+        if (stream->skip_data == NGX_SPDY_DATA_ERROR) {\n+            rc = (r->headers_in.content_length_n == -1)\n+                 ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE\n+                 : NGX_HTTP_BAD_REQUEST;\n+\n+        } else {\n+            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n+        }\n+\n+        ngx_http_finalize_request(r, rc);\n+    }\n+\n+    return ngx_http_spdy_state_skip(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_uint_t               sid, status;\n+    ngx_event_t             *ev;\n+    ngx_connection_t        *fc;\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    if (end - pos < NGX_SPDY_RST_STREAM_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_rst_stream);\n+    }\n+\n+    if (sc->length != NGX_SPDY_RST_STREAM_SIZE) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent RST_STREAM frame with incorrect length %uz\",\n+                      sc->length);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sid = ngx_spdy_frame_parse_sid(pos);\n+\n+    pos += NGX_SPDY_SID_SIZE;\n+\n+    status = ngx_spdy_frame_parse_uint32(pos);\n+\n+    pos += sizeof(uint32_t);\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy RST_STREAM sid:%ui st:%ui\", sid, status);\n+\n+    stream = ngx_http_spdy_get_stream_by_id(sc, sid);\n+\n+    if (stream == NULL) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"unknown spdy stream\");\n+\n+        return ngx_http_spdy_state_complete(sc, pos, end);\n+    }\n+\n+    stream->in_closed = 1;\n+    stream->out_closed = 1;\n+\n+    fc = stream->request->connection;\n+    fc->error = 1;\n+\n+    switch (status) {\n+\n+    case NGX_SPDY_CANCEL:\n+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n+                      \"client canceled stream %ui\", sid);\n+        break;\n+\n+    case NGX_SPDY_INTERNAL_ERROR:\n+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n+                      \"client terminated stream %ui due to internal error\",\n+                      sid);\n+        break;\n+\n+    default:\n+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n+                      \"client terminated stream %ui with status %ui\",\n+                      sid, status);\n+        break;\n+    }\n+\n+    ev = fc->read;\n+    ev->handler(ev);\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    if (end - pos < NGX_SPDY_PING_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_ping);\n+    }\n+\n+    if (sc->length != NGX_SPDY_PING_SIZE) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent PING frame with incorrect length %uz\",\n+                      sc->length);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy PING frame\");\n+\n+    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_PING_SIZE,\n+                                        NGX_SPDY_HIGHEST_PRIORITY);\n+    if (frame == NULL) {\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    buf = frame->first->buf;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_PING);\n+    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_PING_SIZE);\n+\n+    p = ngx_cpymem(p, pos, NGX_SPDY_PING_SIZE);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    pos += NGX_SPDY_PING_SIZE;\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    size_t  size;\n+\n+    size = end - pos;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy frame skip %uz of %uz\", size, sc->length);\n+\n+    if (size < sc->length) {\n+        sc->length -= size;\n+        return ngx_http_spdy_state_save(sc, end, end,\n+                                        ngx_http_spdy_state_skip);\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos + sc->length, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_uint_t  fid, val;\n+\n+    if (sc->entries == 0) {\n+\n+        if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) {\n+            return ngx_http_spdy_state_save(sc, pos, end,\n+                                            ngx_http_spdy_state_settings);\n+        }\n+\n+        sc->entries = ngx_spdy_frame_parse_uint32(pos);\n+\n+        pos += NGX_SPDY_SETTINGS_NUM_SIZE;\n+        sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE;\n+\n+        if (sc->length < sc->entries * NGX_SPDY_SETTINGS_PAIR_SIZE) {\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client sent SETTINGS frame with incorrect \"\n+                          \"length %uz or number of entries %ui\",\n+                          sc->length, sc->entries);\n+\n+            return ngx_http_spdy_state_protocol_error(sc);\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"spdy SETTINGS frame has %ui entries\", sc->entries);\n+    }\n+\n+    while (sc->entries) {\n+        if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) {\n+            return ngx_http_spdy_state_save(sc, pos, end,\n+                                            ngx_http_spdy_state_settings);\n+        }\n+\n+        sc->entries--;\n+        sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE;\n+\n+        fid = ngx_spdy_frame_parse_uint32(pos);\n+\n+        pos += NGX_SPDY_SETTINGS_FID_SIZE;\n+\n+        val = ngx_spdy_frame_parse_uint32(pos);\n+\n+        pos += NGX_SPDY_SETTINGS_VAL_SIZE;\n+\n+        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"spdy SETTINGS entry fl:%ui id:%ui val:%ui\",\n+                       ngx_spdy_frame_flags(fid), ngx_spdy_frame_id(fid), val);\n+\n+        if (ngx_spdy_frame_flags(fid) == NGX_SPDY_SETTINGS_FLAG_PERSISTED) {\n+            continue;\n+        }\n+\n+        switch (ngx_spdy_frame_id(fid)) {\n+\n+        case NGX_SPDY_SETTINGS_INIT_WINDOW:\n+\n+            if (val > NGX_SPDY_MAX_WINDOW) {\n+                ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                              \"client sent SETTINGS frame with \"\n+                              \"incorrect INIT_WINDOW value: %ui\", val);\n+\n+                return ngx_http_spdy_state_protocol_error(sc);\n+            }\n+\n+            if (ngx_http_spdy_adjust_windows(sc, val - sc->init_window)\n+                != NGX_OK)\n+            {\n+                return ngx_http_spdy_state_internal_error(sc);\n+            }\n+\n+            sc->init_window = val;\n+\n+            continue;\n+        }\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy frame complete pos:%p end:%p\", pos, end);\n+\n+    if (pos > end) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,\n+                      \"receive buffer overrun\");\n+\n+        ngx_debug_point();\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    sc->handler = ngx_http_spdy_state_head;\n+    sc->stream = NULL;\n+\n+    return pos;\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler)\n+{\n+    size_t  size;\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy frame state save pos:%p end:%p handler:%p\",\n+                   pos, end, handler);\n+\n+    size = end - pos;\n+\n+    if (size > NGX_SPDY_STATE_BUFFER_SIZE) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,\n+                      \"state buffer overflow: %uz bytes required\", size);\n+\n+        ngx_debug_point();\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE);\n+\n+    sc->buffer_used = size;\n+    sc->handler = handler;\n+    sc->incomplete = 1;\n+\n+    return end;\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_inflate_error(ngx_http_spdy_connection_t *sc, int rc)\n+{\n+    if (rc == Z_DATA_ERROR || rc == Z_STREAM_END) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent SYN_STREAM frame with \"\n+                      \"corrupted header block, inflate() failed: %d\", rc);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    ngx_log_error(NGX_LOG_ERR, sc->connection->log, 0,\n+                  \"inflate() failed: %d\", rc);\n+\n+    return ngx_http_spdy_state_internal_error(sc);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_protocol_error(ngx_http_spdy_connection_t *sc)\n+{\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy state protocol error\");\n+\n+    if (sc->stream) {\n+        sc->stream->out_closed = 1;\n+        ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST);\n+    }\n+\n+    ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+\n+    return NULL;\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_internal_error(ngx_http_spdy_connection_t *sc)\n+{\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy state internal error\");\n+\n+    if (sc->stream) {\n+        sc->stream->out_closed = 1;\n+        ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+    }\n+\n+    ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+\n+    return NULL;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_send_window_update(ngx_http_spdy_connection_t *sc, ngx_uint_t sid,\n+    ngx_uint_t delta)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy send WINDOW_UPDATE sid:%ui delta:%ui\", sid, delta);\n+\n+    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_WINDOW_UPDATE_SIZE,\n+                                        NGX_SPDY_HIGHEST_PRIORITY);\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf = frame->first->buf;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_WINDOW_UPDATE);\n+    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_WINDOW_UPDATE_SIZE);\n+\n+    p = ngx_spdy_frame_write_sid(p, sid);\n+    p = ngx_spdy_frame_aligned_write_uint32(p, delta);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t sid,\n+    ngx_uint_t status, ngx_uint_t priority)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    if (sc->connection->error) {\n+        return NGX_OK;\n+    }\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy send RST_STREAM sid:%ui st:%ui\", sid, status);\n+\n+    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_RST_STREAM_SIZE,\n+                                        priority);\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf = frame->first->buf;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_RST_STREAM);\n+    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_RST_STREAM_SIZE);\n+\n+    p = ngx_spdy_frame_write_sid(p, sid);\n+    p = ngx_spdy_frame_aligned_write_uint32(p, status);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    return NGX_OK;\n+}\n+\n+\n+#if 0\n+static ngx_int_t\n+ngx_http_spdy_send_goaway(ngx_http_spdy_connection_t *sc)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy send GOAWAY sid:%ui\", sc->last_sid);\n+\n+    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_GOAWAY_SIZE,\n+                                        NGX_SPDY_HIGHEST_PRIORITY);\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf = frame->first->buf;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_GOAWAY);\n+    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_GOAWAY_SIZE);\n+\n+    p = ngx_spdy_frame_write_sid(p, sc->last_sid);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    return NGX_OK;\n+}\n+#endif\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_chain_t                *cl;\n+    ngx_http_spdy_srv_conf_t   *sscf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy send SETTINGS frame\");\n+\n+    frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t));\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    cl = ngx_alloc_chain_link(sc->pool);\n+    if (cl == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf = ngx_create_temp_buf(sc->pool, NGX_SPDY_FRAME_HEADER_SIZE\n+                                        + NGX_SPDY_SETTINGS_NUM_SIZE\n+                                        + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE);\n+    if (buf == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf->last_buf = 1;\n+\n+    cl->buf = buf;\n+    cl->next = NULL;\n+\n+    frame->first = cl;\n+    frame->last = cl;\n+    frame->handler = ngx_http_spdy_settings_frame_handler;\n+    frame->stream = NULL;\n+#if (NGX_DEBUG)\n+    frame->length = NGX_SPDY_SETTINGS_NUM_SIZE\n+                    + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE;\n+#endif\n+    frame->priority = NGX_SPDY_HIGHEST_PRIORITY;\n+    frame->blocked = 0;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_SETTINGS);\n+    p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_CLEAR_SETTINGS,\n+                                           NGX_SPDY_SETTINGS_NUM_SIZE\n+                                           + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE);\n+\n+    p = ngx_spdy_frame_aligned_write_uint32(p, 2);\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_MAX_STREAMS);\n+    p = ngx_spdy_frame_aligned_write_uint32(p, sscf->concurrent_streams);\n+\n+    p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_INIT_WINDOW);\n+    p = ngx_spdy_frame_aligned_write_uint32(p, NGX_SPDY_STREAM_WINDOW);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_buf_t  *buf;\n+\n+    buf = frame->first->buf;\n+\n+    if (buf->pos != buf->last) {\n+        return NGX_AGAIN;\n+    }\n+\n+    ngx_free_chain(sc->pool, frame->first);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_http_spdy_out_frame_t *\n+ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t length,\n+    ngx_uint_t priority)\n+{\n+    ngx_chain_t                *cl;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    frame = sc->free_ctl_frames;\n+\n+    if (frame) {\n+        sc->free_ctl_frames = frame->next;\n+\n+        cl = frame->first;\n+        cl->buf->pos = cl->buf->start;\n+\n+    } else {\n+        frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t));\n+        if (frame == NULL) {\n+            return NULL;\n+        }\n+\n+        cl = ngx_alloc_chain_link(sc->pool);\n+        if (cl == NULL) {\n+            return NULL;\n+        }\n+\n+        cl->buf = ngx_create_temp_buf(sc->pool,\n+                                      NGX_SPDY_CTL_FRAME_BUFFER_SIZE);\n+        if (cl->buf == NULL) {\n+            return NULL;\n+        }\n+\n+        cl->buf->last_buf = 1;\n+\n+        frame->first = cl;\n+        frame->last = cl;\n+        frame->handler = ngx_http_spdy_ctl_frame_handler;\n+        frame->stream = NULL;\n+    }\n+\n+#if (NGX_DEBUG)\n+    if (length > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0,\n+                      \"requested control frame is too large: %uz\", length);\n+        return NULL;\n+    }\n+\n+    frame->length = length;\n+#endif\n+\n+    frame->priority = priority;\n+    frame->blocked = 0;\n+\n+    return frame;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_buf_t  *buf;\n+\n+    buf = frame->first->buf;\n+\n+    if (buf->pos != buf->last) {\n+        return NGX_AGAIN;\n+    }\n+\n+    frame->next = sc->free_ctl_frames;\n+    sc->free_ctl_frames = frame;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_http_spdy_stream_t *\n+ngx_http_spdy_create_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t id,\n+    ngx_uint_t priority)\n+{\n+    ngx_log_t                 *log;\n+    ngx_uint_t                 index;\n+    ngx_event_t               *rev, *wev;\n+    ngx_connection_t          *fc;\n+    ngx_http_log_ctx_t        *ctx;\n+    ngx_http_request_t        *r;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_core_srv_conf_t  *cscf;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    fc = sc->free_fake_connections;\n+\n+    if (fc) {\n+        sc->free_fake_connections = fc->data;\n+\n+        rev = fc->read;\n+        wev = fc->write;\n+        log = fc->log;\n+        ctx = log->data;\n+\n+    } else {\n+        fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t));\n+        if (fc == NULL) {\n+            return NULL;\n+        }\n+\n+        rev = ngx_palloc(sc->pool, sizeof(ngx_event_t));\n+        if (rev == NULL) {\n+            return NULL;\n+        }\n+\n+        wev = ngx_palloc(sc->pool, sizeof(ngx_event_t));\n+        if (wev == NULL) {\n+            return NULL;\n+        }\n+\n+        log = ngx_palloc(sc->pool, sizeof(ngx_log_t));\n+        if (log == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx = ngx_palloc(sc->pool, sizeof(ngx_http_log_ctx_t));\n+        if (ctx == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx->connection = fc;\n+        ctx->request = NULL;\n+    }\n+\n+    ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t));\n+\n+    log->data = ctx;\n+\n+    ngx_memzero(rev, sizeof(ngx_event_t));\n+\n+    rev->data = fc;\n+    rev->ready = 1;\n+    rev->handler = ngx_http_spdy_close_stream_handler;\n+    rev->log = log;\n+\n+    ngx_memcpy(wev, rev, sizeof(ngx_event_t));\n+\n+    wev->write = 1;\n+\n+    ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t));\n+\n+    fc->data = sc->http_connection;\n+    fc->read = rev;\n+    fc->write = wev;\n+    fc->sent = 0;\n+    fc->log = log;\n+    fc->buffered = 0;\n+    fc->sndlowat = 1;\n+    fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n+\n+    r = ngx_http_create_request(fc);\n+    if (r == NULL) {\n+        return NULL;\n+    }\n+\n+    r->valid_location = 1;\n+\n+    fc->data = r;\n+    sc->connection->requests++;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    r->header_in = ngx_create_temp_buf(r->pool,\n+                                       cscf->client_header_buffer_size);\n+    if (r->header_in == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n+\n+    stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t));\n+    if (stream == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->spdy_stream = stream;\n+\n+    stream->id = id;\n+    stream->request = r;\n+    stream->connection = sc;\n+\n+    stream->send_window = sc->init_window;\n+    stream->recv_window = NGX_SPDY_STREAM_WINDOW;\n+\n+    stream->priority = priority;\n+\n+    sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module);\n+\n+    index = ngx_http_spdy_stream_index(sscf, id);\n+\n+    stream->index = sc->streams_index[index];\n+    sc->streams_index[index] = stream;\n+\n+    sc->processing++;\n+\n+    return stream;\n+}\n+\n+\n+static ngx_http_spdy_stream_t *\n+ngx_http_spdy_get_stream_by_id(ngx_http_spdy_connection_t *sc,\n+    ngx_uint_t sid)\n+{\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    stream = sc->streams_index[ngx_http_spdy_stream_index(sscf, sid)];\n+\n+    while (stream) {\n+        if (stream->id == sid) {\n+            return stream;\n+        }\n+\n+        stream = stream->index;\n+    }\n+\n+    return NULL;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_header(ngx_http_request_t *r)\n+{\n+    u_char                     *p, *end, ch;\n+    ngx_uint_t                  hash;\n+    ngx_http_core_srv_conf_t   *cscf;\n+\n+    enum {\n+        sw_name_len = 0,\n+        sw_name,\n+        sw_value_len,\n+        sw_value\n+    } state;\n+\n+    state = r->state;\n+\n+    p = r->header_in->pos;\n+    end = r->header_in->last;\n+\n+    switch (state) {\n+\n+    case sw_name_len:\n+\n+        if (end - p < NGX_SPDY_NV_NLEN_SIZE) {\n+            return NGX_AGAIN;\n+        }\n+\n+        r->lowcase_index = ngx_spdy_frame_parse_uint32(p);\n+\n+        if (r->lowcase_index == 0) {\n+            return NGX_ERROR;\n+        }\n+\n+        /* null-terminate the previous header value */\n+        *p = '\\0';\n+\n+        p += NGX_SPDY_NV_NLEN_SIZE;\n+\n+        r->invalid_header = 0;\n+\n+        state = sw_name;\n+\n+        /* fall through */\n+\n+    case sw_name:\n+\n+        if ((ngx_uint_t) (end - p) < r->lowcase_index) {\n+            break;\n+        }\n+\n+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+        r->header_name_start = p;\n+        r->header_name_end = p + r->lowcase_index;\n+\n+        if (p[0] == ':') {\n+            p++;\n+        }\n+\n+        hash = 0;\n+\n+        for ( /* void */ ; p != r->header_name_end; p++) {\n+\n+            ch = *p;\n+\n+            hash = ngx_hash(hash, ch);\n+\n+            if ((ch >= 'a' && ch <= 'z')\n+                || (ch == '-')\n+                || (ch >= '0' && ch <= '9')\n+                || (ch == '_' && cscf->underscores_in_headers))\n+            {\n+                continue;\n+            }\n+\n+            switch (ch) {\n+            case '\\0':\n+            case LF:\n+            case CR:\n+            case ':':\n+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                              \"client sent invalid header name: \\\"%*s\\\"\",\n+                              r->lowcase_index, r->header_name_start);\n+\n+                return NGX_HTTP_PARSE_INVALID_HEADER;\n+            }\n+\n+            if (ch >= 'A' && ch <= 'Z') {\n+                return NGX_ERROR;\n+            }\n+\n+            r->invalid_header = 1;\n+        }\n+\n+        r->header_hash = hash;\n+\n+        state = sw_value_len;\n+\n+        /* fall through */\n+\n+    case sw_value_len:\n+\n+        if (end - p < NGX_SPDY_NV_VLEN_SIZE) {\n+            break;\n+        }\n+\n+        r->lowcase_index = ngx_spdy_frame_parse_uint32(p);\n+\n+        /* null-terminate header name */\n+        *p = '\\0';\n+\n+        p += NGX_SPDY_NV_VLEN_SIZE;\n+\n+        state = sw_value;\n+\n+        /* fall through */\n+\n+    case sw_value:\n+\n+        if ((ngx_uint_t) (end - p) < r->lowcase_index) {\n+            break;\n+        }\n+\n+        r->header_start = p;\n+\n+        while (r->lowcase_index--) {\n+            ch = *p;\n+\n+            if (ch == '\\0') {\n+\n+                if (p == r->header_start) {\n+                    return NGX_ERROR;\n+                }\n+\n+                r->header_end = p;\n+                r->header_in->pos = p + 1;\n+\n+                r->state = sw_value;\n+\n+                return NGX_OK;\n+            }\n+\n+            if (ch == CR || ch == LF) {\n+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                              \"client sent header \\\"%*s\\\" with \"\n+                              \"invalid value: \\\"%*s\\\\%c...\\\"\",\n+                              r->header_name_end - r->header_name_start,\n+                              r->header_name_start,\n+                              p - r->header_start,\n+                              r->header_start,\n+                              ch == CR ? 'r' : 'n');\n+\n+                return NGX_HTTP_PARSE_INVALID_HEADER;\n+            }\n+\n+            p++;\n+        }\n+\n+        r->header_end = p;\n+        r->header_in->pos = p;\n+\n+        r->state = 0;\n+\n+        return NGX_DONE;\n+    }\n+\n+    r->header_in->pos = p;\n+    r->state = state;\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r)\n+{\n+    u_char                    *old, *new, *p;\n+    size_t                     rest;\n+    ngx_buf_t                 *buf;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy alloc large header buffer\");\n+\n+    stream = r->spdy_stream;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    if (stream->header_buffers\n+        == (ngx_uint_t) cscf->large_client_header_buffers.num)\n+    {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent too large request\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    rest = r->header_in->last - r->header_in->pos;\n+\n+    /*\n+     * One more byte is needed for null-termination\n+     * and another one for further progress.\n+     */\n+    if (rest > cscf->large_client_header_buffers.size - 2) {\n+        p = r->header_in->pos;\n+\n+        if (rest > NGX_MAX_ERROR_STR - 300) {\n+            rest = NGX_MAX_ERROR_STR - 300;\n+        }\n+\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent too long header name or value: \\\"%*s...\\\"\",\n+                      rest, p);\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    buf = ngx_create_temp_buf(r->pool, cscf->large_client_header_buffers.size);\n+    if (buf == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy large header alloc: %p %uz\",\n+                   buf->pos, buf->end - buf->last);\n+\n+    old = r->header_in->pos;\n+    new = buf->pos;\n+\n+    if (rest) {\n+        buf->last = ngx_cpymem(new, old, rest);\n+    }\n+\n+    r->header_in = buf;\n+\n+    stream->header_buffers++;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_handle_request_header(ngx_http_request_t *r)\n+{\n+    ngx_uint_t                       i;\n+    ngx_table_elt_t                 *h;\n+    ngx_http_core_srv_conf_t        *cscf;\n+    ngx_http_spdy_request_header_t  *sh;\n+\n+    if (r->invalid_header) {\n+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+        if (cscf->ignore_invalid_headers) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid header: \\\"%*s\\\"\",\n+                          r->header_end - r->header_name_start,\n+                          r->header_name_start);\n+            return NGX_OK;\n+        }\n+\n+    }\n+\n+    if (r->header_name_start[0] == ':') {\n+        r->header_name_start++;\n+\n+        for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {\n+            sh = &ngx_http_spdy_request_headers[i];\n+\n+            if (sh->hash != r->header_hash\n+                || sh->len != r->header_name_end - r->header_name_start\n+                || ngx_strncmp(sh->header, r->header_name_start, sh->len) != 0)\n+            {\n+                continue;\n+            }\n+\n+            return sh->handler(r);\n+        }\n+\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent invalid header name: \\\":%*s\\\"\",\n+                      r->header_end - r->header_name_start,\n+                      r->header_name_start);\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    h->hash = r->header_hash;\n+\n+    h->key.len = r->header_name_end - r->header_name_start;\n+    h->key.data = r->header_name_start;\n+\n+    h->value.len = r->header_end - r->header_start;\n+    h->value.data = r->header_start;\n+\n+    h->lowcase_key = h->key.data;\n+\n+    return NGX_OK;\n+}\n+\n+\n+void\n+ngx_http_spdy_request_headers_init(void)\n+{\n+    ngx_uint_t                       i;\n+    ngx_http_spdy_request_header_t  *h;\n+\n+    for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {\n+        h = &ngx_http_spdy_request_headers[i];\n+        h->hash = ngx_hash_key(h->header, h->len);\n+    }\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_method(ngx_http_request_t *r)\n+{\n+    size_t         k, len;\n+    ngx_uint_t     n;\n+    const u_char  *p, *m;\n+\n+    /*\n+     * This array takes less than 256 sequential bytes,\n+     * and if typical CPU cache line size is 64 bytes,\n+     * it is prefetched for 4 load operations.\n+     */\n+    static const struct {\n+        u_char            len;\n+        const u_char      method[11];\n+        uint32_t          value;\n+    } tests[] = {\n+        { 3, \"GET\",       NGX_HTTP_GET },\n+        { 4, \"POST\",      NGX_HTTP_POST },\n+        { 4, \"HEAD\",      NGX_HTTP_HEAD },\n+        { 7, \"OPTIONS\",   NGX_HTTP_OPTIONS },\n+        { 8, \"PROPFIND\",  NGX_HTTP_PROPFIND },\n+        { 3, \"PUT\",       NGX_HTTP_PUT },\n+        { 5, \"MKCOL\",     NGX_HTTP_MKCOL },\n+        { 6, \"DELETE\",    NGX_HTTP_DELETE },\n+        { 4, \"COPY\",      NGX_HTTP_COPY },\n+        { 4, \"MOVE\",      NGX_HTTP_MOVE },\n+        { 9, \"PROPPATCH\", NGX_HTTP_PROPPATCH },\n+        { 4, \"LOCK\",      NGX_HTTP_LOCK },\n+        { 6, \"UNLOCK\",    NGX_HTTP_UNLOCK },\n+        { 5, \"PATCH\",     NGX_HTTP_PATCH },\n+        { 5, \"TRACE\",     NGX_HTTP_TRACE }\n+    }, *test;\n+\n+    if (r->method_name.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :method header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    len = r->header_end - r->header_start;\n+\n+    r->method_name.len = len;\n+    r->method_name.data = r->header_start;\n+\n+    test = tests;\n+    n = sizeof(tests) / sizeof(tests[0]);\n+\n+    do {\n+        if (len == test->len) {\n+            p = r->method_name.data;\n+            m = test->method;\n+            k = len;\n+\n+            do {\n+                if (*p++ != *m++) {\n+                    goto next;\n+                }\n+            } while (--k);\n+\n+            r->method = test->value;\n+            return NGX_OK;\n+        }\n+\n+    next:\n+        test++;\n+\n+    } while (--n);\n+\n+    p = r->method_name.data;\n+\n+    do {\n+        if ((*p < 'A' || *p > 'Z') && *p != '_') {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid method: \\\"%V\\\"\",\n+                          &r->method_name);\n+\n+            return NGX_HTTP_PARSE_INVALID_HEADER;\n+        }\n+\n+        p++;\n+\n+    } while (--len);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_scheme(ngx_http_request_t *r)\n+{\n+    if (r->schema_start) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :schema header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    r->schema_start = r->header_start;\n+    r->schema_end = r->header_end;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_host(ngx_http_request_t *r)\n+{\n+    ngx_table_elt_t  *h;\n+\n+    if (r->headers_in.host) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :host header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    r->headers_in.host = h;\n+\n+    h->hash = r->header_hash;\n+\n+    h->key.len = r->header_name_end - r->header_name_start;\n+    h->key.data = r->header_name_start;\n+\n+    h->value.len = r->header_end - r->header_start;\n+    h->value.data = r->header_start;\n+\n+    h->lowcase_key = h->key.data;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_path(ngx_http_request_t *r)\n+{\n+    if (r->unparsed_uri.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :path header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    r->uri_start = r->header_start;\n+    r->uri_end = r->header_end;\n+\n+    if (ngx_http_parse_uri(r) != NGX_OK) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent invalid URI: \\\"%*s\\\"\",\n+                      r->uri_end - r->uri_start, r->uri_start);\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    if (ngx_http_process_request_uri(r) != NGX_OK) {\n+        /*\n+         * request has been finalized already\n+         * in ngx_http_process_request_uri()\n+         */\n+        return NGX_ABORT;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_version(ngx_http_request_t *r)\n+{\n+    u_char  *p, ch;\n+\n+    if (r->http_protocol.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :version header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    p = r->header_start;\n+\n+    if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) {\n+        goto invalid;\n+    }\n+\n+    ch = *(p + 5);\n+\n+    if (ch < '1' || ch > '9') {\n+        goto invalid;\n+    }\n+\n+    r->http_major = ch - '0';\n+\n+    for (p += 6; p != r->header_end - 2; p++) {\n+\n+        ch = *p;\n+\n+        if (ch == '.') {\n+            break;\n+        }\n+\n+        if (ch < '0' || ch > '9') {\n+            goto invalid;\n+        }\n+\n+        r->http_major = r->http_major * 10 + ch - '0';\n+    }\n+\n+    if (*p != '.') {\n+        goto invalid;\n+    }\n+\n+    ch = *(p + 1);\n+\n+    if (ch < '0' || ch > '9') {\n+        goto invalid;\n+    }\n+\n+    r->http_minor = ch - '0';\n+\n+    for (p += 2; p != r->header_end; p++) {\n+\n+        ch = *p;\n+\n+        if (ch < '0' || ch > '9') {\n+            goto invalid;\n+        }\n+\n+        r->http_minor = r->http_minor * 10 + ch - '0';\n+    }\n+\n+    r->http_protocol.len = r->header_end - r->header_start;\n+    r->http_protocol.data = r->header_start;\n+    r->http_version = r->http_major * 1000 + r->http_minor;\n+\n+    return NGX_OK;\n+\n+invalid:\n+\n+    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                  \"client sent invalid http version: \\\"%*s\\\"\",\n+                  r->header_end - r->header_start, r->header_start);\n+\n+    return NGX_HTTP_PARSE_INVALID_HEADER;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_construct_request_line(ngx_http_request_t *r)\n+{\n+    u_char  *p;\n+\n+    if (r->method_name.len == 0\n+        || r->unparsed_uri.len == 0\n+        || r->http_protocol.len == 0)\n+    {\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.len = r->method_name.len + 1\n+                          + r->unparsed_uri.len + 1\n+                          + r->http_protocol.len;\n+\n+    p = ngx_pnalloc(r->pool, r->request_line.len + 1);\n+    if (p == NULL) {\n+        ngx_http_spdy_close_stream(r->spdy_stream,\n+                                   NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.data = p;\n+\n+    p = ngx_cpymem(p, r->method_name.data, r->method_name.len);\n+\n+    *p++ = ' ';\n+\n+    p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);\n+\n+    *p++ = ' ';\n+\n+    ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1);\n+\n+    /* some modules expect the space character after method name */\n+    r->method_name.data = r->request_line.data;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_spdy_run_request(ngx_http_request_t *r)\n+{\n+    ngx_uint_t                  i;\n+    ngx_list_part_t            *part;\n+    ngx_table_elt_t            *h;\n+    ngx_http_header_t          *hh;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    if (ngx_http_spdy_construct_request_line(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy http request line: \\\"%V\\\"\", &r->request_line);\n+\n+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+    part = &r->headers_in.headers.part;\n+    h = part->elts;\n+\n+    for (i = 0 ;; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            h = part->elts;\n+            i = 0;\n+        }\n+\n+        hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash,\n+                           h[i].lowcase_key, h[i].key.len);\n+\n+        if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) {\n+            return;\n+        }\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"spdy http header: \\\"%V: %V\\\"\", &h[i].key, &h[i].value);\n+    }\n+\n+    r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;\n+\n+    if (ngx_http_process_request_header(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    if (r->headers_in.content_length_n > 0 && r->spdy_stream->in_closed) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client prematurely closed stream\");\n+\n+        r->spdy_stream->skip_data = NGX_SPDY_DATA_ERROR;\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return;\n+    }\n+\n+    ngx_http_process_request(r);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_init_request_body(ngx_http_request_t *r)\n+{\n+    ngx_buf_t                 *buf;\n+    ngx_temp_file_t           *tf;\n+    ngx_http_request_body_t   *rb;\n+    ngx_http_core_loc_conf_t  *clcf;\n+\n+    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));\n+    if (rb == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_body = rb;\n+\n+    if (r->spdy_stream->in_closed) {\n+        return NGX_OK;\n+    }\n+\n+    rb->rest = r->headers_in.content_length_n;\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    if (r->request_body_in_file_only\n+        || rb->rest > (off_t) clcf->client_body_buffer_size\n+        || rb->rest < 0)\n+    {\n+        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));\n+        if (tf == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        tf->file.fd = NGX_INVALID_FILE;\n+        tf->file.log = r->connection->log;\n+        tf->path = clcf->client_body_temp_path;\n+        tf->pool = r->pool;\n+        tf->warn = \"a client request body is buffered to a temporary file\";\n+        tf->log_level = r->request_body_file_log_level;\n+        tf->persistent = r->request_body_in_persistent_file;\n+        tf->clean = r->request_body_in_clean_file;\n+\n+        if (r->request_body_file_group_access) {\n+            tf->access = 0660;\n+        }\n+\n+        rb->temp_file = tf;\n+\n+        if (r->spdy_stream->in_closed\n+            && ngx_create_temp_file(&tf->file, tf->path, tf->pool,\n+                                    tf->persistent, tf->clean, tf->access)\n+               != NGX_OK)\n+        {\n+            return NGX_ERROR;\n+        }\n+\n+        buf = ngx_calloc_buf(r->pool);\n+        if (buf == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+    } else {\n+\n+        if (rb->rest == 0) {\n+            return NGX_OK;\n+        }\n+\n+        buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest);\n+        if (buf == NULL) {\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    rb->buf = buf;\n+\n+    rb->bufs = ngx_alloc_chain_link(r->pool);\n+    if (rb->bufs == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    rb->bufs->buf = buf;\n+    rb->bufs->next = NULL;\n+\n+    rb->rest = 0;\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_http_spdy_read_request_body(ngx_http_request_t *r,\n+    ngx_http_client_body_handler_pt post_handler)\n+{\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy read request body\");\n+\n+    stream = r->spdy_stream;\n+\n+    switch (stream->skip_data) {\n+\n+    case NGX_SPDY_DATA_DISCARD:\n+        post_handler(r);\n+        return NGX_OK;\n+\n+    case NGX_SPDY_DATA_ERROR:\n+        if (r->headers_in.content_length_n == -1) {\n+            return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;\n+        } else {\n+            return NGX_HTTP_BAD_REQUEST;\n+        }\n+\n+    case NGX_SPDY_DATA_INTERNAL_ERROR:\n+        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n+    }\n+\n+    if (!r->request_body->buf && ngx_http_spdy_init_request_body(r) != NGX_OK) {\n+        stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;\n+        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n+    }\n+\n+    if (stream->in_closed) {\n+        post_handler(r);\n+        return NGX_OK;\n+    }\n+\n+    r->request_body->post_handler = post_handler;\n+\n+    r->read_event_handler = ngx_http_test_reading;\n+    r->write_event_handler = ngx_http_request_empty_handler;\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream, ngx_uint_t status)\n+{\n+    ngx_event_t       *rev;\n+    ngx_connection_t  *fc;\n+\n+    if (ngx_http_spdy_send_rst_stream(sc, stream->id, status,\n+                                      NGX_SPDY_HIGHEST_PRIORITY)\n+        == NGX_ERROR)\n+    {\n+        return NGX_ERROR;\n+    }\n+\n+    stream->out_closed = 1;\n+\n+    fc = stream->request->connection;\n+    fc->error = 1;\n+\n+    rev = fc->read;\n+    rev->handler(rev);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_spdy_close_stream_handler(ngx_event_t *ev)\n+{\n+    ngx_connection_t    *fc;\n+    ngx_http_request_t  *r;\n+\n+    fc = ev->data;\n+    r = fc->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy close stream handler\");\n+\n+    ngx_http_spdy_close_stream(r->spdy_stream, 0);\n+}\n+\n+\n+void\n+ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc)\n+{\n+    ngx_event_t                  *ev;\n+    ngx_connection_t             *fc;\n+    ngx_http_spdy_stream_t      **index, *s;\n+    ngx_http_spdy_srv_conf_t     *sscf;\n+    ngx_http_spdy_connection_t   *sc;\n+\n+    sc = stream->connection;\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy close stream %ui, queued %ui, processing %ui\",\n+                   stream->id, stream->queued, sc->processing);\n+\n+    fc = stream->request->connection;\n+\n+    if (stream->queued) {\n+        fc->write->handler = ngx_http_spdy_close_stream_handler;\n+        return;\n+    }\n+\n+    if (!stream->out_closed) {\n+        if (ngx_http_spdy_send_rst_stream(sc, stream->id,\n+                                          NGX_SPDY_INTERNAL_ERROR,\n+                                          stream->priority)\n+            != NGX_OK)\n+        {\n+            sc->connection->error = 1;\n+        }\n+    }\n+\n+    if (sc->stream == stream) {\n+        sc->stream = NULL;\n+    }\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id);\n+\n+    for ( ;; ) {\n+        s = *index;\n+\n+        if (s == NULL) {\n+            break;\n+        }\n+\n+        if (s == stream) {\n+            *index = s->index;\n+            break;\n+        }\n+\n+        index = &s->index;\n+    }\n+\n+    ngx_http_free_request(stream->request, rc);\n+\n+    ev = fc->read;\n+\n+    if (ev->active || ev->disabled) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,\n+                      \"fake read event was activated\");\n+    }\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    ev = fc->write;\n+\n+    if (ev->active || ev->disabled) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,\n+                      \"fake write event was activated\");\n+    }\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    fc->data = sc->free_fake_connections;\n+    sc->free_fake_connections = fc;\n+\n+    sc->processing--;\n+\n+    if (sc->processing || sc->blocked) {\n+        return;\n+    }\n+\n+    ev = sc->connection->read;\n+\n+    ev->handler = ngx_http_spdy_handle_connection_handler;\n+    ngx_post_event(ev, &ngx_posted_events);\n+}\n+\n+\n+static void\n+ngx_http_spdy_handle_connection_handler(ngx_event_t *rev)\n+{\n+    ngx_connection_t  *c;\n+\n+    rev->handler = ngx_http_spdy_read_handler;\n+\n+    if (rev->ready) {\n+        ngx_http_spdy_read_handler(rev);\n+        return;\n+    }\n+\n+    c = rev->data;\n+\n+    ngx_http_spdy_handle_connection(c->data);\n+}\n+\n+\n+static void\n+ngx_http_spdy_keepalive_handler(ngx_event_t *rev)\n+{\n+    ngx_connection_t            *c;\n+    ngx_http_spdy_srv_conf_t    *sscf;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    c = rev->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"spdy keepalive handler\");\n+\n+    if (rev->timedout || c->close) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+#if (NGX_HAVE_KQUEUE)\n+\n+    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n+        if (rev->pending_eof) {\n+            c->log->handler = NULL;\n+            ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,\n+                          \"kevent() reported that client %V closed \"\n+                          \"keepalive connection\", &c->addr_text);\n+#if (NGX_HTTP_SSL)\n+            if (c->ssl) {\n+                c->ssl->no_send_shutdown = 1;\n+            }\n+#endif\n+            ngx_http_close_connection(c);\n+            return;\n+        }\n+    }\n+\n+#endif\n+\n+    c->destroyed = 0;\n+    c->idle = 0;\n+    ngx_reusable_connection(c, 0);\n+\n+    sc = c->data;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);\n+    if (sc->pool == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->streams_index = ngx_pcalloc(sc->pool,\n+                                    ngx_http_spdy_streams_index_size(sscf)\n+                                    * sizeof(ngx_http_spdy_stream_t *));\n+    if (sc->streams_index == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    c->write->handler = ngx_http_spdy_write_handler;\n+\n+    rev->handler = ngx_http_spdy_read_handler;\n+    ngx_http_spdy_read_handler(rev);\n+}\n+\n+\n+static void\n+ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,\n+    ngx_int_t rc)\n+{\n+    ngx_uint_t                 i, size;\n+    ngx_event_t               *ev;\n+    ngx_connection_t          *c, *fc;\n+    ngx_http_request_t        *r;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    c = sc->connection;\n+\n+    if (!sc->processing) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    c->error = 1;\n+    c->read->handler = ngx_http_empty_handler;\n+    c->write->handler = ngx_http_empty_handler;\n+\n+    sc->last_out = NULL;\n+\n+    sc->blocked = 1;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    size = ngx_http_spdy_streams_index_size(sscf);\n+\n+    for (i = 0; i < size; i++) {\n+        stream = sc->streams_index[i];\n+\n+        while (stream) {\n+            stream->handled = 0;\n+\n+            r = stream->request;\n+            fc = r->connection;\n+\n+            fc->error = 1;\n+\n+            if (stream->queued) {\n+                stream->queued = 0;\n+\n+                ev = fc->write;\n+                ev->delayed = 0;\n+\n+            } else {\n+                ev = fc->read;\n+            }\n+\n+            stream = stream->index;\n+\n+            ev->eof = 1;\n+            ev->handler(ev);\n+        }\n+    }\n+\n+    sc->blocked = 0;\n+\n+    if (sc->processing) {\n+        return;\n+    }\n+\n+    ngx_http_close_connection(c);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc, ssize_t delta)\n+{\n+    ngx_uint_t                 i, size;\n+    ngx_event_t               *wev;\n+    ngx_http_spdy_stream_t    *stream, *sn;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    size = ngx_http_spdy_streams_index_size(sscf);\n+\n+    for (i = 0; i < size; i++) {\n+\n+        for (stream = sc->streams_index[i]; stream; stream = sn) {\n+            sn = stream->index;\n+\n+            if (delta > 0\n+                && stream->send_window\n+                      > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta))\n+            {\n+                if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                                   NGX_SPDY_FLOW_CONTROL_ERROR)\n+                    == NGX_ERROR)\n+                {\n+                    return NGX_ERROR;\n+                }\n+\n+                continue;\n+            }\n+\n+            stream->send_window += delta;\n+\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"spdy:%ui adjust window:%z\",\n+                           stream->id, stream->send_window);\n+\n+            if (stream->send_window > 0 && stream->exhausted) {\n+                stream->exhausted = 0;\n+\n+                wev = stream->request->connection->write;\n+\n+                if (!wev->timer_set) {\n+                    wev->delayed = 0;\n+                    wev->handler(wev);\n+                }\n+            }\n+        }\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_spdy_pool_cleanup(void *data)\n+{\n+    ngx_http_spdy_connection_t  *sc = data;\n+\n+    if (sc->pool) {\n+        ngx_destroy_pool(sc->pool);\n+    }\n+}\n+\n+\n+static void *\n+ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size)\n+{\n+    ngx_http_spdy_connection_t *sc = opaque;\n+\n+    return ngx_palloc(sc->connection->pool, items * size);\n+}\n+\n+\n+static void\n+ngx_http_spdy_zfree(void *opaque, void *address)\n+{\n+#if 0\n+    ngx_http_spdy_connection_t *sc = opaque;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy zfree: %p\", address);\n+#endif\n+}\ndiff -uNr a/src/http/ngx_http_spdy_filter_module.c b/src/http/ngx_http_spdy_filter_module.c\n--- a/src/http/ngx_http_spdy_filter_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy_filter_module.c\t2019-10-17 22:51:22.620255620 +0800\n@@ -0,0 +1,1222 @@\n+\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <nginx.h>\n+#include <ngx_http_spdy_module.h>\n+\n+#include <zlib.h>\n+\n+\n+#define ngx_http_spdy_nv_nsize(h)  (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1)\n+#define ngx_http_spdy_nv_vsize(h)  (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1)\n+\n+#define ngx_http_spdy_nv_write_num   ngx_spdy_frame_write_uint32\n+#define ngx_http_spdy_nv_write_nlen  ngx_spdy_frame_write_uint32\n+#define ngx_http_spdy_nv_write_vlen  ngx_spdy_frame_write_uint32\n+\n+#define ngx_http_spdy_nv_write_name(p, h)                                     \\\n+    ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1)\n+\n+#define ngx_http_spdy_nv_write_val(p, h)                                      \\\n+    ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1)\n+\n+\n+static ngx_chain_t *ngx_http_spdy_send_chain(ngx_connection_t *fc,\n+    ngx_chain_t *in, off_t limit);\n+\n+static ngx_inline ngx_int_t ngx_http_spdy_filter_send(\n+    ngx_connection_t *fc, ngx_http_spdy_stream_t *stream);\n+static ngx_inline ngx_int_t ngx_http_spdy_flow_control(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);\n+static void ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream);\n+\n+static ngx_chain_t *ngx_http_spdy_filter_get_shadow(\n+    ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);\n+static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame(\n+    ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first,\n+    ngx_chain_t *last);\n+\n+static ngx_int_t ngx_http_spdy_syn_frame_handler(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);\n+static ngx_int_t ngx_http_spdy_data_frame_handler(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);\n+static ngx_inline void ngx_http_spdy_handle_frame(\n+    ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame);\n+static ngx_inline void ngx_http_spdy_handle_stream(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);\n+\n+static void ngx_http_spdy_filter_cleanup(void *data);\n+\n+static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf);\n+\n+\n+static ngx_http_module_t  ngx_http_spdy_filter_module_ctx = {\n+    NULL,                                  /* preconfiguration */\n+    ngx_http_spdy_filter_init,             /* postconfiguration */\n+\n+    NULL,                                  /* create main configuration */\n+    NULL,                                  /* init main configuration */\n+\n+    NULL,                                  /* create server configuration */\n+    NULL,                                  /* merge server configuration */\n+\n+    NULL,                                  /* create location configuration */\n+    NULL                                   /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_spdy_filter_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_spdy_filter_module_ctx,      /* module context */\n+    NULL,                                  /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_header_filter(ngx_http_request_t *r)\n+{\n+    int                           rc;\n+    size_t                        len;\n+    u_char                       *p, *buf, *last;\n+    ngx_buf_t                    *b;\n+    ngx_str_t                     host;\n+    ngx_uint_t                    i, j, count, port;\n+    ngx_chain_t                  *cl;\n+    ngx_list_part_t              *part, *pt;\n+    ngx_table_elt_t              *header, *h;\n+    ngx_connection_t             *c;\n+    ngx_http_cleanup_t           *cln;\n+    ngx_http_core_loc_conf_t     *clcf;\n+    ngx_http_core_srv_conf_t     *cscf;\n+    ngx_http_spdy_stream_t       *stream;\n+    ngx_http_spdy_out_frame_t    *frame;\n+    ngx_http_spdy_connection_t   *sc;\n+    struct sockaddr_in           *sin;\n+#if (NGX_HAVE_INET6)\n+    struct sockaddr_in6          *sin6;\n+#endif\n+    u_char                        addr[NGX_SOCKADDR_STRLEN];\n+\n+    if (!r->spdy_stream) {\n+        return ngx_http_next_header_filter(r);\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy header filter\");\n+\n+    if (r->header_sent) {\n+        return NGX_OK;\n+    }\n+\n+    r->header_sent = 1;\n+\n+    if (r != r->main) {\n+        return NGX_OK;\n+    }\n+\n+    c = r->connection;\n+\n+    if (r->method == NGX_HTTP_HEAD) {\n+        r->header_only = 1;\n+    }\n+\n+    switch (r->headers_out.status) {\n+\n+    case NGX_HTTP_OK:\n+    case NGX_HTTP_PARTIAL_CONTENT:\n+        break;\n+\n+    case NGX_HTTP_NOT_MODIFIED:\n+        r->header_only = 1;\n+        break;\n+\n+    case NGX_HTTP_NO_CONTENT:\n+        r->header_only = 1;\n+\n+        ngx_str_null(&r->headers_out.content_type);\n+\n+        r->headers_out.content_length = NULL;\n+        r->headers_out.content_length_n = -1;\n+\n+        /* fall through */\n+\n+    default:\n+        r->headers_out.last_modified_time = -1;\n+        r->headers_out.last_modified = NULL;\n+    }\n+\n+    len = NGX_SPDY_NV_NUM_SIZE\n+          + ngx_http_spdy_nv_nsize(\":version\")\n+          + ngx_http_spdy_nv_vsize(\"HTTP/1.1\")\n+          + ngx_http_spdy_nv_nsize(\":status\")\n+          + (r->headers_out.status_line.len\n+             ? NGX_SPDY_NV_VLEN_SIZE + r->headers_out.status_line.len\n+             : ngx_http_spdy_nv_vsize(\"418\"));\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    if (r->headers_out.server == NULL) {\n+        len += ngx_http_spdy_nv_nsize(\"server\");\n+        len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER)\n+                                   : ngx_http_spdy_nv_vsize(\"nginx\");\n+    }\n+\n+    if (r->headers_out.date == NULL) {\n+        len += ngx_http_spdy_nv_nsize(\"date\")\n+               + ngx_http_spdy_nv_vsize(\"Wed, 31 Dec 1986 10:00:00 GMT\");\n+    }\n+\n+    if (r->headers_out.content_type.len) {\n+        len += ngx_http_spdy_nv_nsize(\"content-type\")\n+               + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len;\n+\n+        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n+            && r->headers_out.charset.len)\n+        {\n+            len += sizeof(\"; charset=\") - 1 + r->headers_out.charset.len;\n+        }\n+    }\n+\n+    if (r->headers_out.content_length == NULL\n+        && r->headers_out.content_length_n >= 0)\n+    {\n+        len += ngx_http_spdy_nv_nsize(\"content-length\")\n+               + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN;\n+    }\n+\n+    if (r->headers_out.last_modified == NULL\n+        && r->headers_out.last_modified_time != -1)\n+    {\n+        len += ngx_http_spdy_nv_nsize(\"last-modified\")\n+               + ngx_http_spdy_nv_vsize(\"Wed, 31 Dec 1986 10:00:00 GMT\");\n+    }\n+\n+    if (r->headers_out.location\n+        && r->headers_out.location->value.len\n+        && r->headers_out.location->value.data[0] == '/')\n+    {\n+        r->headers_out.location->hash = 0;\n+\n+        if (clcf->server_name_in_redirect) {\n+            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+            host = cscf->server_name;\n+\n+        } else if (r->headers_in.server.len) {\n+            host = r->headers_in.server;\n+\n+        } else {\n+            host.len = NGX_SOCKADDR_STRLEN;\n+            host.data = addr;\n+\n+            if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {\n+                return NGX_ERROR;\n+            }\n+        }\n+\n+        switch (c->local_sockaddr->sa_family) {\n+\n+#if (NGX_HAVE_INET6)\n+        case AF_INET6:\n+            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;\n+            port = ntohs(sin6->sin6_port);\n+            break;\n+#endif\n+#if (NGX_HAVE_UNIX_DOMAIN)\n+        case AF_UNIX:\n+            port = 0;\n+            break;\n+#endif\n+        default: /* AF_INET */\n+            sin = (struct sockaddr_in *) c->local_sockaddr;\n+            port = ntohs(sin->sin_port);\n+            break;\n+        }\n+\n+        len += ngx_http_spdy_nv_nsize(\"location\")\n+               + ngx_http_spdy_nv_vsize(\"https://\")\n+               + host.len\n+               + r->headers_out.location->value.len;\n+\n+        if (clcf->port_in_redirect) {\n+\n+#if (NGX_HTTP_SSL)\n+            if (c->ssl)\n+                port = (port == 443) ? 0 : port;\n+            else\n+#endif\n+                port = (port == 80) ? 0 : port;\n+\n+        } else {\n+            port = 0;\n+        }\n+\n+        if (port) {\n+            len += sizeof(\":65535\") - 1;\n+        }\n+\n+    } else {\n+        ngx_str_null(&host);\n+        port = 0;\n+    }\n+\n+    part = &r->headers_out.headers.part;\n+    header = part->elts;\n+\n+    for (i = 0; /* void */; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            header = part->elts;\n+            i = 0;\n+        }\n+\n+        if (header[i].hash == 0) {\n+            continue;\n+        }\n+\n+        len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len\n+               + NGX_SPDY_NV_VLEN_SIZE  + header[i].value.len;\n+    }\n+\n+    buf = ngx_alloc(len, r->pool->log);\n+    if (buf == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    last = buf + NGX_SPDY_NV_NUM_SIZE;\n+\n+    last = ngx_http_spdy_nv_write_name(last, \":version\");\n+    last = ngx_http_spdy_nv_write_val(last, \"HTTP/1.1\");\n+\n+    last = ngx_http_spdy_nv_write_name(last, \":status\");\n+\n+    if (r->headers_out.status_line.len) {\n+        last = ngx_http_spdy_nv_write_vlen(last,\n+                                           r->headers_out.status_line.len);\n+        last = ngx_cpymem(last, r->headers_out.status_line.data,\n+                          r->headers_out.status_line.len);\n+    } else {\n+        last = ngx_http_spdy_nv_write_vlen(last, 3);\n+        last = ngx_sprintf(last, \"%03ui\", r->headers_out.status);\n+    }\n+\n+    count = 2;\n+\n+    if (r->headers_out.server == NULL) {\n+        last = ngx_http_spdy_nv_write_name(last, \"server\");\n+        last = clcf->server_tokens\n+               ? ngx_http_spdy_nv_write_val(last, NGINX_VER)\n+               : ngx_http_spdy_nv_write_val(last, \"nginx\");\n+\n+        count++;\n+    }\n+\n+    if (r->headers_out.date == NULL) {\n+        last = ngx_http_spdy_nv_write_name(last, \"date\");\n+\n+        last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len);\n+\n+        last = ngx_cpymem(last, ngx_cached_http_time.data,\n+                          ngx_cached_http_time.len);\n+\n+        count++;\n+    }\n+\n+    if (r->headers_out.content_type.len) {\n+\n+        last = ngx_http_spdy_nv_write_name(last, \"content-type\");\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_cpymem(p, r->headers_out.content_type.data,\n+                          r->headers_out.content_type.len);\n+\n+        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n+            && r->headers_out.charset.len)\n+        {\n+            last = ngx_cpymem(last, \"; charset=\", sizeof(\"; charset=\") - 1);\n+\n+            last = ngx_cpymem(last, r->headers_out.charset.data,\n+                              r->headers_out.charset.len);\n+\n+            /* update r->headers_out.content_type for possible logging */\n+\n+            r->headers_out.content_type.len = last - p;\n+            r->headers_out.content_type.data = p;\n+        }\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           r->headers_out.content_type.len);\n+\n+        count++;\n+    }\n+\n+    if (r->headers_out.content_length == NULL\n+        && r->headers_out.content_length_n >= 0)\n+    {\n+        last = ngx_http_spdy_nv_write_name(last, \"content-length\");\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_sprintf(p, \"%O\", r->headers_out.content_length_n);\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           last - p);\n+\n+        count++;\n+    }\n+\n+    if (r->headers_out.last_modified == NULL\n+        && r->headers_out.last_modified_time != -1)\n+    {\n+        last = ngx_http_spdy_nv_write_name(last, \"last-modified\");\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_http_time(p, r->headers_out.last_modified_time);\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           last - p);\n+\n+        count++;\n+    }\n+\n+    if (host.data) {\n+\n+        last = ngx_http_spdy_nv_write_name(last, \"location\");\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_cpymem(p, \"http\", sizeof(\"http\") - 1);\n+\n+#if (NGX_HTTP_SSL)\n+        if (c->ssl) {\n+            *last++ ='s';\n+        }\n+#endif\n+\n+        *last++ = ':'; *last++ = '/'; *last++ = '/';\n+\n+        last = ngx_cpymem(last, host.data, host.len);\n+\n+        if (port) {\n+            last = ngx_sprintf(last, \":%ui\", port);\n+        }\n+\n+        last = ngx_cpymem(last, r->headers_out.location->value.data,\n+                          r->headers_out.location->value.len);\n+\n+        /* update r->headers_out.location->value for possible logging */\n+\n+        r->headers_out.location->value.len = last - p;\n+        r->headers_out.location->value.data = p;\n+        ngx_str_set(&r->headers_out.location->key, \"location\");\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           r->headers_out.location->value.len);\n+\n+        count++;\n+    }\n+\n+    part = &r->headers_out.headers.part;\n+    header = part->elts;\n+\n+    for (i = 0; /* void */; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            header = part->elts;\n+            i = 0;\n+        }\n+\n+        if (header[i].hash == 0 || header[i].hash == 2) {\n+            continue;\n+        }\n+\n+        last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len);\n+\n+        ngx_strlow(last, header[i].key.data, header[i].key.len);\n+        last += header[i].key.len;\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_cpymem(p, header[i].value.data, header[i].value.len);\n+\n+        pt = part;\n+        h = header;\n+\n+        for (j = i + 1; /* void */; j++) {\n+\n+            if (j >= pt->nelts) {\n+                if (pt->next == NULL) {\n+                    break;\n+                }\n+\n+                pt = pt->next;\n+                h = pt->elts;\n+                j = 0;\n+            }\n+\n+            if (h[j].hash == 0 || h[j].hash == 2\n+                || h[j].key.len != header[i].key.len\n+                || ngx_strncasecmp(header[i].key.data, h[j].key.data,\n+                                   header[i].key.len))\n+            {\n+                continue;\n+            }\n+\n+            if (h[j].value.len) {\n+                if (last != p) {\n+                    *last++ = '\\0';\n+                }\n+\n+                last = ngx_cpymem(last, h[j].value.data, h[j].value.len);\n+            }\n+\n+            h[j].hash = 2;\n+        }\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           last - p);\n+\n+        count++;\n+    }\n+\n+    (void) ngx_http_spdy_nv_write_num(buf, count);\n+\n+    stream = r->spdy_stream;\n+    sc = stream->connection;\n+\n+    len = last - buf;\n+\n+    b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE\n+                                     + NGX_SPDY_SYN_REPLY_SIZE\n+                                     + deflateBound(&sc->zstream_out, len));\n+    if (b == NULL) {\n+        ngx_free(buf);\n+        return NGX_ERROR;\n+    }\n+\n+    b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE;\n+\n+    sc->zstream_out.next_in = buf;\n+    sc->zstream_out.avail_in = len;\n+    sc->zstream_out.next_out = b->last;\n+    sc->zstream_out.avail_out = b->end - b->last;\n+\n+    rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH);\n+\n+    ngx_free(buf);\n+\n+    if (rc != Z_OK) {\n+        ngx_log_error(NGX_LOG_ALERT, c->log, 0, \"deflate() failed: %d\", rc);\n+        return NGX_ERROR;\n+    }\n+\n+    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                   \"spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n+                   sc->zstream_out.next_in, sc->zstream_out.next_out,\n+                   sc->zstream_out.avail_in, sc->zstream_out.avail_out,\n+                   rc);\n+\n+    b->last = sc->zstream_out.next_out;\n+\n+    p = b->pos;\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY);\n+\n+    len = b->last - b->pos;\n+\n+    r->header_size = len;\n+\n+    len -= NGX_SPDY_FRAME_HEADER_SIZE;\n+\n+    if (r->header_only) {\n+        b->last_buf = 1;\n+        p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, len);\n+\n+    } else {\n+        p = ngx_spdy_frame_write_flags_and_len(p, 0, len);\n+    }\n+\n+    (void) ngx_spdy_frame_write_sid(p, stream->id);\n+\n+    cl = ngx_alloc_chain_link(r->pool);\n+    if (cl == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    cl->buf = b;\n+    cl->next = NULL;\n+\n+    frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t));\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    frame->first = cl;\n+    frame->last = cl;\n+    frame->handler = ngx_http_spdy_syn_frame_handler;\n+    frame->stream = stream;\n+    frame->length = len;\n+    frame->priority = stream->priority;\n+    frame->blocked = 1;\n+    frame->fin = r->header_only;\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,\n+                   \"spdy:%ui create SYN_REPLY frame %p: len:%uz\",\n+                   stream->id, frame, frame->length);\n+\n+    ngx_http_spdy_queue_blocked_frame(sc, frame);\n+\n+    cln = ngx_http_cleanup_add(r, 0);\n+    if (cln == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    cln->handler = ngx_http_spdy_filter_cleanup;\n+    cln->data = stream;\n+\n+    stream->queued = 1;\n+\n+    c->send_chain = ngx_http_spdy_send_chain;\n+    c->need_last_buf = 1;\n+\n+    return ngx_http_spdy_filter_send(c, stream);\n+}\n+\n+\n+static ngx_chain_t *\n+ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)\n+{\n+    off_t                        size, offset;\n+    size_t                       rest, frame_size;\n+    ngx_chain_t                 *cl, *out, **ln;\n+    ngx_http_request_t          *r;\n+    ngx_http_spdy_stream_t      *stream;\n+    ngx_http_spdy_loc_conf_t    *slcf;\n+    ngx_http_spdy_out_frame_t   *frame;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    r = fc->data;\n+    stream = r->spdy_stream;\n+\n+#if (NGX_SUPPRESS_WARN)\n+    size = 0;\n+#endif\n+\n+    while (in) {\n+        size = ngx_buf_size(in->buf);\n+\n+        if (size || in->buf->last_buf) {\n+            break;\n+        }\n+\n+        in = in->next;\n+    }\n+\n+    if (in == NULL) {\n+\n+        if (stream->queued) {\n+            fc->write->delayed = 1;\n+        } else {\n+            fc->buffered &= ~NGX_SPDY_BUFFERED;\n+        }\n+\n+        return NULL;\n+    }\n+\n+    sc = stream->connection;\n+\n+    if (size && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {\n+        fc->write->delayed = 1;\n+        return in;\n+    }\n+\n+    if (limit == 0 || limit > (off_t) sc->send_window) {\n+        limit = sc->send_window;\n+    }\n+\n+    if (limit > stream->send_window) {\n+        limit = (stream->send_window > 0) ? stream->send_window : 0;\n+    }\n+\n+    if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {\n+        cl = ngx_alloc_chain_link(r->pool);\n+        if (cl == NULL) {\n+            return NGX_CHAIN_ERROR;\n+        }\n+\n+        cl->buf = in->buf;\n+        in->buf = cl->buf->shadow;\n+\n+        offset = ngx_buf_in_memory(in->buf)\n+                 ? (cl->buf->pos - in->buf->pos)\n+                 : (cl->buf->file_pos - in->buf->file_pos);\n+\n+        cl->next = stream->free_bufs;\n+        stream->free_bufs = cl;\n+\n+    } else {\n+        offset = 0;\n+    }\n+\n+#if (NGX_SUPPRESS_WARN)\n+    cl = NULL;\n+#endif\n+\n+    slcf = ngx_http_get_module_loc_conf(r, ngx_http_spdy_module);\n+\n+    frame_size = (limit <= (off_t) slcf->chunk_size) ? (size_t) limit\n+                                                     : slcf->chunk_size;\n+\n+    for ( ;; ) {\n+        ln = &out;\n+        rest = frame_size;\n+\n+        while ((off_t) rest >= size) {\n+\n+            if (offset) {\n+                cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,\n+                                                     offset, size);\n+                if (cl == NULL) {\n+                    return NGX_CHAIN_ERROR;\n+                }\n+\n+                offset = 0;\n+\n+            } else {\n+                cl = ngx_alloc_chain_link(r->pool);\n+                if (cl == NULL) {\n+                    return NGX_CHAIN_ERROR;\n+                }\n+\n+                cl->buf = in->buf;\n+            }\n+\n+            *ln = cl;\n+            ln = &cl->next;\n+\n+            rest -= (size_t) size;\n+            in = in->next;\n+\n+            if (in == NULL) {\n+                frame_size -= rest;\n+                rest = 0;\n+                break;\n+            }\n+\n+            size = ngx_buf_size(in->buf);\n+        }\n+\n+        if (rest) {\n+            cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,\n+                                                 offset, rest);\n+            if (cl == NULL) {\n+                return NGX_CHAIN_ERROR;\n+            }\n+\n+            cl->buf->flush = 0;\n+            cl->buf->last_buf = 0;\n+\n+            *ln = cl;\n+\n+            offset += rest;\n+            size -= rest;\n+        }\n+\n+        frame = ngx_http_spdy_filter_get_data_frame(stream, frame_size,\n+                                                    out, cl);\n+        if (frame == NULL) {\n+            return NGX_CHAIN_ERROR;\n+        }\n+\n+        ngx_http_spdy_queue_frame(sc, frame);\n+\n+        sc->send_window -= frame_size;\n+\n+        stream->send_window -= frame_size;\n+        stream->queued++;\n+\n+        if (in == NULL) {\n+            break;\n+        }\n+\n+        limit -= frame_size;\n+\n+        if (limit == 0) {\n+            break;\n+        }\n+\n+        if (limit < (off_t) slcf->chunk_size) {\n+            frame_size = (size_t) limit;\n+        }\n+    }\n+\n+    if (offset) {\n+        cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size);\n+        if (cl == NULL) {\n+            return NGX_CHAIN_ERROR;\n+        }\n+\n+        in->buf = cl->buf;\n+        ngx_free_chain(r->pool, cl);\n+    }\n+\n+    if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) {\n+        return NGX_CHAIN_ERROR;\n+    }\n+\n+    if (in && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {\n+        fc->write->delayed = 1;\n+    }\n+\n+    return in;\n+}\n+\n+\n+static ngx_chain_t *\n+ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf,\n+    off_t offset, off_t size)\n+{\n+    ngx_buf_t    *chunk;\n+    ngx_chain_t  *cl;\n+\n+    cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);\n+    if (cl == NULL) {\n+        return NULL;\n+    }\n+\n+    chunk = cl->buf;\n+\n+    ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));\n+\n+    chunk->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow;\n+    chunk->shadow = buf;\n+\n+    if (ngx_buf_in_memory(chunk)) {\n+        chunk->pos += offset;\n+        chunk->last = chunk->pos + size;\n+    }\n+\n+    if (chunk->in_file) {\n+        chunk->file_pos += offset;\n+        chunk->file_last = chunk->file_pos + size;\n+    }\n+\n+    return cl;\n+}\n+\n+\n+static ngx_http_spdy_out_frame_t *\n+ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream,\n+    size_t len, ngx_chain_t *first, ngx_chain_t *last)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_uint_t                  flags;\n+    ngx_chain_t                *cl;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+\n+    frame = stream->free_frames;\n+\n+    if (frame) {\n+        stream->free_frames = frame->next;\n+\n+    } else {\n+        frame = ngx_palloc(stream->request->pool,\n+                           sizeof(ngx_http_spdy_out_frame_t));\n+        if (frame == NULL) {\n+            return NULL;\n+        }\n+    }\n+\n+    flags = last->buf->last_buf ? NGX_SPDY_FLAG_FIN : 0;\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,\n+                   \"spdy:%ui create DATA frame %p: len:%uz flags:%ui\",\n+                   stream->id, frame, len, flags);\n+\n+    cl = ngx_chain_get_free_buf(stream->request->pool,\n+                                &stream->free_data_headers);\n+    if (cl == NULL) {\n+        return NULL;\n+    }\n+\n+    buf = cl->buf;\n+\n+    if (buf->start) {\n+        p = buf->start;\n+        buf->pos = p;\n+\n+        p += NGX_SPDY_SID_SIZE;\n+\n+        (void) ngx_spdy_frame_write_flags_and_len(p, flags, len);\n+\n+    } else {\n+        p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE);\n+        if (p == NULL) {\n+            return NULL;\n+        }\n+\n+        buf->pos = p;\n+        buf->start = p;\n+\n+        p = ngx_spdy_frame_write_sid(p, stream->id);\n+        p = ngx_spdy_frame_write_flags_and_len(p, flags, len);\n+\n+        buf->last = p;\n+        buf->end = p;\n+\n+        buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame;\n+        buf->memory = 1;\n+    }\n+\n+    cl->next = first;\n+    first = cl;\n+\n+    last->buf->flush = 1;\n+\n+    frame->first = first;\n+    frame->last = last;\n+    frame->handler = ngx_http_spdy_data_frame_handler;\n+    frame->stream = stream;\n+    frame->length = len;\n+    frame->priority = stream->priority;\n+    frame->blocked = 0;\n+    frame->fin = last->buf->last_buf;\n+\n+    return frame;\n+}\n+\n+\n+static ngx_inline ngx_int_t\n+ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream)\n+{\n+    stream->blocked = 1;\n+\n+    if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) {\n+        fc->error = 1;\n+        return NGX_ERROR;\n+    }\n+\n+    stream->blocked = 0;\n+\n+    if (stream->queued) {\n+        fc->buffered |= NGX_SPDY_BUFFERED;\n+        fc->write->delayed = 1;\n+        return NGX_AGAIN;\n+    }\n+\n+    fc->buffered &= ~NGX_SPDY_BUFFERED;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_inline ngx_int_t\n+ngx_http_spdy_flow_control(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream)\n+{\n+    if (stream->send_window <= 0) {\n+        stream->exhausted = 1;\n+        return NGX_DECLINED;\n+    }\n+\n+    if (sc->send_window == 0) {\n+        ngx_http_spdy_waiting_queue(sc, stream);\n+        return NGX_DECLINED;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream)\n+{\n+    ngx_queue_t             *q;\n+    ngx_http_spdy_stream_t  *s;\n+\n+    if (stream->handled) {\n+        return;\n+    }\n+\n+    stream->handled = 1;\n+\n+    for (q = ngx_queue_last(&sc->waiting);\n+         q != ngx_queue_sentinel(&sc->waiting);\n+         q = ngx_queue_prev(q))\n+    {\n+        s = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);\n+\n+        /*\n+         * NB: higher values represent lower priorities.\n+         */\n+        if (stream->priority >= s->priority) {\n+            break;\n+        }\n+    }\n+\n+    ngx_queue_insert_after(q, &stream->queue);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_buf_t               *buf;\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    buf = frame->first->buf;\n+\n+    if (buf->pos != buf->last) {\n+        return NGX_AGAIN;\n+    }\n+\n+    stream = frame->stream;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy:%ui SYN_REPLY frame %p was sent\", stream->id, frame);\n+\n+    ngx_free_chain(stream->request->pool, frame->first);\n+\n+    ngx_http_spdy_handle_frame(stream, frame);\n+\n+    ngx_http_spdy_handle_stream(sc, stream);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_buf_t               *buf;\n+    ngx_chain_t             *cl, *ln;\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    stream = frame->stream;\n+\n+    cl = frame->first;\n+\n+    if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame) {\n+\n+        if (cl->buf->pos != cl->buf->last) {\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"spdy:%ui DATA frame %p was sent partially\",\n+                           stream->id, frame);\n+\n+            return NGX_AGAIN;\n+        }\n+\n+        ln = cl->next;\n+\n+        cl->next = stream->free_data_headers;\n+        stream->free_data_headers = cl;\n+\n+        if (cl == frame->last) {\n+            goto done;\n+        }\n+\n+        cl = ln;\n+    }\n+\n+    for ( ;; ) {\n+        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {\n+            buf = cl->buf->shadow;\n+\n+            if (ngx_buf_in_memory(buf)) {\n+                buf->pos = cl->buf->pos;\n+            }\n+\n+            if (buf->in_file) {\n+                buf->file_pos = cl->buf->file_pos;\n+            }\n+        }\n+\n+        if (ngx_buf_size(cl->buf) != 0) {\n+\n+            if (cl != frame->first) {\n+                frame->first = cl;\n+                ngx_http_spdy_handle_stream(sc, stream);\n+            }\n+\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"spdy:%ui DATA frame %p was sent partially\",\n+                           stream->id, frame);\n+\n+            return NGX_AGAIN;\n+        }\n+\n+        ln = cl->next;\n+\n+        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {\n+            cl->next = stream->free_bufs;\n+            stream->free_bufs = cl;\n+\n+        } else {\n+            ngx_free_chain(stream->request->pool, cl);\n+        }\n+\n+        if (cl == frame->last) {\n+            goto done;\n+        }\n+\n+        cl = ln;\n+    }\n+\n+done:\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy:%ui DATA frame %p was sent\", stream->id, frame);\n+\n+    stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE;\n+\n+    ngx_http_spdy_handle_frame(stream, frame);\n+\n+    ngx_http_spdy_handle_stream(sc, stream);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_inline void\n+ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_http_request_t  *r;\n+\n+    r = stream->request;\n+\n+    r->connection->sent += NGX_SPDY_FRAME_HEADER_SIZE + frame->length;\n+\n+    if (frame->fin) {\n+        stream->out_closed = 1;\n+    }\n+\n+    frame->next = stream->free_frames;\n+    stream->free_frames = frame;\n+\n+    stream->queued--;\n+}\n+\n+\n+static ngx_inline void\n+ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream)\n+{\n+    ngx_event_t  *wev;\n+\n+    if (stream->handled || stream->blocked || stream->exhausted) {\n+        return;\n+    }\n+\n+    wev = stream->request->connection->write;\n+\n+    /*\n+     * This timer can only be set if the stream was delayed because of rate\n+     * limit.  In that case the event should be triggered by the timer.\n+     */\n+\n+    if (!wev->timer_set) {\n+        wev->delayed = 0;\n+\n+        stream->handled = 1;\n+        ngx_queue_insert_tail(&sc->posted, &stream->queue);\n+    }\n+}\n+\n+\n+static void\n+ngx_http_spdy_filter_cleanup(void *data)\n+{\n+    ngx_http_spdy_stream_t *stream = data;\n+\n+    size_t                       delta;\n+    ngx_http_spdy_out_frame_t   *frame, **fn;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    if (stream->handled) {\n+        stream->handled = 0;\n+        ngx_queue_remove(&stream->queue);\n+    }\n+\n+    if (stream->queued == 0) {\n+        return;\n+    }\n+\n+    delta = 0;\n+    sc = stream->connection;\n+    fn = &sc->last_out;\n+\n+    for ( ;; ) {\n+        frame = *fn;\n+\n+        if (frame == NULL) {\n+            break;\n+        }\n+\n+        if (frame->stream == stream && !frame->blocked) {\n+            *fn = frame->next;\n+\n+            delta += frame->length;\n+\n+            if (--stream->queued == 0) {\n+                break;\n+            }\n+\n+            continue;\n+        }\n+\n+        fn = &frame->next;\n+    }\n+\n+    if (sc->send_window == 0 && delta && !ngx_queue_empty(&sc->waiting)) {\n+        ngx_queue_add(&sc->posted, &sc->waiting);\n+        ngx_queue_init(&sc->waiting);\n+    }\n+\n+    sc->send_window += delta;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_filter_init(ngx_conf_t *cf)\n+{\n+    ngx_http_next_header_filter = ngx_http_top_header_filter;\n+    ngx_http_top_header_filter = ngx_http_spdy_header_filter;\n+\n+    return NGX_OK;\n+}\ndiff -uNr a/src/http/ngx_http_spdy.h b/src/http/ngx_http_spdy.h\n--- a/src/http/ngx_http_spdy.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy.h\t2019-10-17 22:51:22.620255620 +0800\n@@ -0,0 +1,261 @@\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#ifndef _NGX_HTTP_SPDY_H_INCLUDED_\n+#define _NGX_HTTP_SPDY_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+\n+#include <zlib.h>\n+\n+\n+#define NGX_SPDY_VERSION              3\n+\n+#define NGX_SPDY_NPN_ADVERTISE        \"\\x08spdy/3.1\"\n+#define NGX_SPDY_NPN_NEGOTIATED       \"spdy/3.1\"\n+\n+#define NGX_SPDY_STATE_BUFFER_SIZE    16\n+\n+#define NGX_SPDY_CTL_BIT              1\n+\n+#define NGX_SPDY_SYN_STREAM           1\n+#define NGX_SPDY_SYN_REPLY            2\n+#define NGX_SPDY_RST_STREAM           3\n+#define NGX_SPDY_SETTINGS             4\n+#define NGX_SPDY_PING                 6\n+#define NGX_SPDY_GOAWAY               7\n+#define NGX_SPDY_HEADERS              8\n+#define NGX_SPDY_WINDOW_UPDATE        9\n+\n+#define NGX_SPDY_FRAME_HEADER_SIZE    8\n+\n+#define NGX_SPDY_SID_SIZE             4\n+#define NGX_SPDY_DELTA_SIZE           4\n+\n+#define NGX_SPDY_SYN_STREAM_SIZE      10\n+#define NGX_SPDY_SYN_REPLY_SIZE       4\n+#define NGX_SPDY_RST_STREAM_SIZE      8\n+#define NGX_SPDY_PING_SIZE            4\n+#define NGX_SPDY_GOAWAY_SIZE          8\n+#define NGX_SPDY_WINDOW_UPDATE_SIZE   8\n+#define NGX_SPDY_NV_NUM_SIZE          4\n+#define NGX_SPDY_NV_NLEN_SIZE         4\n+#define NGX_SPDY_NV_VLEN_SIZE         4\n+#define NGX_SPDY_SETTINGS_NUM_SIZE    4\n+#define NGX_SPDY_SETTINGS_FID_SIZE    4\n+#define NGX_SPDY_SETTINGS_VAL_SIZE    4\n+\n+#define NGX_SPDY_SETTINGS_PAIR_SIZE                                           \\\n+    (NGX_SPDY_SETTINGS_FID_SIZE + NGX_SPDY_SETTINGS_VAL_SIZE)\n+\n+#define NGX_SPDY_HIGHEST_PRIORITY     0\n+#define NGX_SPDY_LOWEST_PRIORITY      7\n+\n+#define NGX_SPDY_FLAG_FIN             0x01\n+#define NGX_SPDY_FLAG_UNIDIRECTIONAL  0x02\n+#define NGX_SPDY_FLAG_CLEAR_SETTINGS  0x01\n+\n+#define NGX_SPDY_MAX_FRAME_SIZE       ((1 << 24) - 1)\n+\n+#define NGX_SPDY_DATA_DISCARD         1\n+#define NGX_SPDY_DATA_ERROR           2\n+#define NGX_SPDY_DATA_INTERNAL_ERROR  3\n+\n+\n+typedef struct ngx_http_spdy_connection_s   ngx_http_spdy_connection_t;\n+typedef struct ngx_http_spdy_out_frame_s    ngx_http_spdy_out_frame_t;\n+\n+\n+typedef u_char *(*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+\n+struct ngx_http_spdy_connection_s {\n+    ngx_connection_t                *connection;\n+    ngx_http_connection_t           *http_connection;\n+\n+    ngx_uint_t                       processing;\n+\n+    size_t                           send_window;\n+    size_t                           recv_window;\n+    size_t                           init_window;\n+\n+    ngx_queue_t                      waiting;\n+\n+    u_char                           buffer[NGX_SPDY_STATE_BUFFER_SIZE];\n+    size_t                           buffer_used;\n+    ngx_http_spdy_handler_pt         handler;\n+\n+    z_stream                         zstream_in;\n+    z_stream                         zstream_out;\n+\n+    ngx_pool_t                      *pool;\n+\n+    ngx_http_spdy_out_frame_t       *free_ctl_frames;\n+    ngx_connection_t                *free_fake_connections;\n+\n+    ngx_http_spdy_stream_t         **streams_index;\n+\n+    ngx_http_spdy_out_frame_t       *last_out;\n+\n+    ngx_queue_t                      posted;\n+\n+    ngx_http_spdy_stream_t          *stream;\n+\n+    ngx_uint_t                       entries;\n+    size_t                           length;\n+    u_char                           flags;\n+\n+    ngx_uint_t                       last_sid;\n+\n+    unsigned                         blocked:1;\n+    unsigned                         incomplete:1;\n+};\n+\n+\n+struct ngx_http_spdy_stream_s {\n+    ngx_uint_t                       id;\n+    ngx_http_request_t              *request;\n+    ngx_http_spdy_connection_t      *connection;\n+    ngx_http_spdy_stream_t          *index;\n+\n+    ngx_uint_t                       header_buffers;\n+    ngx_uint_t                       queued;\n+\n+    /*\n+     * A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the\n+     * send_window to become negative, hence it's signed.\n+     */\n+    ssize_t                          send_window;\n+    size_t                           recv_window;\n+\n+    ngx_http_spdy_out_frame_t       *free_frames;\n+    ngx_chain_t                     *free_data_headers;\n+    ngx_chain_t                     *free_bufs;\n+\n+    ngx_queue_t                      queue;\n+\n+    unsigned                         priority:3;\n+    unsigned                         handled:1;\n+    unsigned                         blocked:1;\n+    unsigned                         exhausted:1;\n+    unsigned                         in_closed:1;\n+    unsigned                         out_closed:1;\n+    unsigned                         skip_data:2;\n+};\n+\n+\n+struct ngx_http_spdy_out_frame_s {\n+    ngx_http_spdy_out_frame_t       *next;\n+    ngx_chain_t                     *first;\n+    ngx_chain_t                     *last;\n+    ngx_int_t                      (*handler)(ngx_http_spdy_connection_t *sc,\n+                                        ngx_http_spdy_out_frame_t *frame);\n+\n+    ngx_http_spdy_stream_t          *stream;\n+    size_t                           length;\n+\n+    ngx_uint_t                       priority;\n+    unsigned                         blocked:1;\n+    unsigned                         fin:1;\n+};\n+\n+\n+static ngx_inline void\n+ngx_http_spdy_queue_frame(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_http_spdy_out_frame_t  **out;\n+\n+    for (out = &sc->last_out; *out; out = &(*out)->next)\n+    {\n+        /*\n+         * NB: higher values represent lower priorities.\n+         */\n+        if (frame->priority >= (*out)->priority) {\n+            break;\n+        }\n+    }\n+\n+    frame->next = *out;\n+    *out = frame;\n+}\n+\n+\n+static ngx_inline void\n+ngx_http_spdy_queue_blocked_frame(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_http_spdy_out_frame_t  **out;\n+\n+    for (out = &sc->last_out; *out; out = &(*out)->next)\n+    {\n+        if ((*out)->blocked) {\n+            break;\n+        }\n+    }\n+\n+    frame->next = *out;\n+    *out = frame;\n+}\n+\n+\n+void ngx_http_spdy_init(ngx_event_t *rev);\n+void ngx_http_spdy_request_headers_init(void);\n+\n+ngx_int_t ngx_http_spdy_read_request_body(ngx_http_request_t *r,\n+    ngx_http_client_body_handler_pt post_handler);\n+\n+void ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc);\n+\n+ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc);\n+\n+\n+#define ngx_spdy_frame_aligned_write_uint16(p, s)                             \\\n+    (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))\n+\n+#define ngx_spdy_frame_aligned_write_uint32(p, s)                             \\\n+    (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))\n+\n+#if (NGX_HAVE_NONALIGNED)\n+\n+#define ngx_spdy_frame_write_uint16  ngx_spdy_frame_aligned_write_uint16\n+#define ngx_spdy_frame_write_uint32  ngx_spdy_frame_aligned_write_uint32\n+\n+#else\n+\n+#define ngx_spdy_frame_write_uint16(p, s)                                     \\\n+    ((p)[0] = (u_char) ((s) >> 8),                                            \\\n+     (p)[1] = (u_char)  (s),                                                  \\\n+     (p) + sizeof(uint16_t))\n+\n+#define ngx_spdy_frame_write_uint32(p, s)                                     \\\n+    ((p)[0] = (u_char) ((s) >> 24),                                           \\\n+     (p)[1] = (u_char) ((s) >> 16),                                           \\\n+     (p)[2] = (u_char) ((s) >> 8),                                            \\\n+     (p)[3] = (u_char)  (s),                                                  \\\n+     (p) + sizeof(uint32_t))\n+\n+#endif\n+\n+\n+#define ngx_spdy_ctl_frame_head(t)                                            \\\n+    ((uint32_t) NGX_SPDY_CTL_BIT << 31 | NGX_SPDY_VERSION << 16 | (t))\n+\n+#define ngx_spdy_frame_write_head(p, t)                                       \\\n+    ngx_spdy_frame_aligned_write_uint32(p, ngx_spdy_ctl_frame_head(t))\n+\n+#define ngx_spdy_frame_write_flags_and_len(p, f, l)                           \\\n+    ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (l))\n+#define ngx_spdy_frame_write_flags_and_id(p, f, i)                            \\\n+    ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (i))\n+\n+#define ngx_spdy_frame_write_sid     ngx_spdy_frame_aligned_write_uint32\n+#define ngx_spdy_frame_write_window  ngx_spdy_frame_aligned_write_uint32\n+\n+#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */\ndiff -uNr a/src/http/ngx_http_spdy_module.c b/src/http/ngx_http_spdy_module.c\n--- a/src/http/ngx_http_spdy_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy_module.c\t2019-10-17 22:51:22.621255628 +0800\n@@ -0,0 +1,408 @@\n+\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_spdy_module.h>\n+\n+\n+static ngx_int_t ngx_http_spdy_add_variables(ngx_conf_t *cf);\n+\n+static ngx_int_t ngx_http_spdy_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data);\n+static ngx_int_t ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data);\n+\n+static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle);\n+\n+static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf);\n+static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf);\n+static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf);\n+static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent,\n+    void *child);\n+static void *ngx_http_spdy_create_loc_conf(ngx_conf_t *cf);\n+static char *ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent,\n+    void *child);\n+\n+static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post,\n+    void *data);\n+static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data);\n+static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post,\n+    void *data);\n+static char *ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data);\n+\n+\n+static ngx_conf_num_bounds_t  ngx_http_spdy_headers_comp_bounds = {\n+    ngx_conf_check_num_bounds, 0, 9\n+};\n+\n+static ngx_conf_post_t  ngx_http_spdy_recv_buffer_size_post =\n+    { ngx_http_spdy_recv_buffer_size };\n+static ngx_conf_post_t  ngx_http_spdy_pool_size_post =\n+    { ngx_http_spdy_pool_size };\n+static ngx_conf_post_t  ngx_http_spdy_streams_index_mask_post =\n+    { ngx_http_spdy_streams_index_mask };\n+static ngx_conf_post_t  ngx_http_spdy_chunk_size_post =\n+    { ngx_http_spdy_chunk_size };\n+\n+\n+static ngx_command_t  ngx_http_spdy_commands[] = {\n+\n+    { ngx_string(\"spdy_recv_buffer_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_MAIN_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_main_conf_t, recv_buffer_size),\n+      &ngx_http_spdy_recv_buffer_size_post },\n+\n+    { ngx_string(\"spdy_pool_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, pool_size),\n+      &ngx_http_spdy_pool_size_post },\n+\n+    { ngx_string(\"spdy_max_concurrent_streams\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, concurrent_streams),\n+      NULL },\n+\n+    { ngx_string(\"spdy_streams_index_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, streams_index_mask),\n+      &ngx_http_spdy_streams_index_mask_post },\n+\n+    { ngx_string(\"spdy_recv_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, recv_timeout),\n+      NULL },\n+\n+    { ngx_string(\"spdy_keepalive_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, keepalive_timeout),\n+      NULL },\n+\n+    { ngx_string(\"spdy_headers_comp\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, headers_comp),\n+      &ngx_http_spdy_headers_comp_bounds },\n+\n+    { ngx_string(\"spdy_chunk_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_LOC_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_loc_conf_t, chunk_size),\n+      &ngx_http_spdy_chunk_size_post },\n+\n+      ngx_null_command\n+};\n+\n+\n+static ngx_http_module_t  ngx_http_spdy_module_ctx = {\n+    ngx_http_spdy_add_variables,           /* preconfiguration */\n+    NULL,                                  /* postconfiguration */\n+\n+    ngx_http_spdy_create_main_conf,        /* create main configuration */\n+    ngx_http_spdy_init_main_conf,          /* init main configuration */\n+\n+    ngx_http_spdy_create_srv_conf,         /* create server configuration */\n+    ngx_http_spdy_merge_srv_conf,          /* merge server configuration */\n+\n+    ngx_http_spdy_create_loc_conf,         /* create location configuration */\n+    ngx_http_spdy_merge_loc_conf           /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_spdy_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_spdy_module_ctx,             /* module context */\n+    ngx_http_spdy_commands,                /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    ngx_http_spdy_module_init,             /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_variable_t  ngx_http_spdy_vars[] = {\n+\n+    { ngx_string(\"spdy\"), NULL,\n+      ngx_http_spdy_variable, 0, 0, 0 },\n+\n+    { ngx_string(\"spdy_request_priority\"), NULL,\n+      ngx_http_spdy_request_priority_variable, 0, 0, 0 },\n+\n+    { ngx_null_string, NULL, NULL, 0, 0, 0 }\n+};\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_add_variables(ngx_conf_t *cf)\n+{\n+   ngx_http_variable_t  *var, *v;\n+\n+    for (v = ngx_http_spdy_vars; v->name.len; v++) {\n+        var = ngx_http_add_variable(cf, &v->name, v->flags);\n+        if (var == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        var->get_handler = v->get_handler;\n+        var->data = v->data;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data)\n+{\n+    if (r->spdy_stream) {\n+        v->len = sizeof(\"3.1\") - 1;\n+        v->valid = 1;\n+        v->no_cacheable = 0;\n+        v->not_found = 0;\n+        v->data = (u_char *) \"3.1\";\n+\n+        return NGX_OK;\n+    }\n+\n+    *v = ngx_http_variable_null_value;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data)\n+{\n+    if (r->spdy_stream) {\n+        v->len = 1;\n+        v->valid = 1;\n+        v->no_cacheable = 0;\n+        v->not_found = 0;\n+\n+        v->data = ngx_pnalloc(r->pool, 1);\n+        if (v->data == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        v->data[0] = '0' + (u_char) r->spdy_stream->priority;\n+\n+        return NGX_OK;\n+    }\n+\n+    *v = ngx_http_variable_null_value;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_module_init(ngx_cycle_t *cycle)\n+{\n+    ngx_http_spdy_request_headers_init();\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void *\n+ngx_http_spdy_create_main_conf(ngx_conf_t *cf)\n+{\n+    ngx_http_spdy_main_conf_t  *smcf;\n+\n+    smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_main_conf_t));\n+    if (smcf == NULL) {\n+        return NULL;\n+    }\n+\n+    smcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;\n+\n+    return smcf;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf)\n+{\n+    ngx_http_spdy_main_conf_t *smcf = conf;\n+\n+    ngx_conf_init_size_value(smcf->recv_buffer_size, 256 * 1024);\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static void *\n+ngx_http_spdy_create_srv_conf(ngx_conf_t *cf)\n+{\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_srv_conf_t));\n+    if (sscf == NULL) {\n+        return NULL;\n+    }\n+\n+    sscf->pool_size = NGX_CONF_UNSET_SIZE;\n+\n+    sscf->concurrent_streams = NGX_CONF_UNSET_UINT;\n+    sscf->streams_index_mask = NGX_CONF_UNSET_UINT;\n+\n+    sscf->recv_timeout = NGX_CONF_UNSET_MSEC;\n+    sscf->keepalive_timeout = NGX_CONF_UNSET_MSEC;\n+\n+    sscf->headers_comp = NGX_CONF_UNSET;\n+\n+    return sscf;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n+{\n+    ngx_http_spdy_srv_conf_t *prev = parent;\n+    ngx_http_spdy_srv_conf_t *conf = child;\n+\n+    ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);\n+\n+    ngx_conf_merge_uint_value(conf->concurrent_streams,\n+                              prev->concurrent_streams, 100);\n+\n+    ngx_conf_merge_uint_value(conf->streams_index_mask,\n+                              prev->streams_index_mask, 32 - 1);\n+\n+    ngx_conf_merge_msec_value(conf->recv_timeout,\n+                              prev->recv_timeout, 30000);\n+    ngx_conf_merge_msec_value(conf->keepalive_timeout,\n+                              prev->keepalive_timeout, 180000);\n+\n+    ngx_conf_merge_value(conf->headers_comp, prev->headers_comp, 0);\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static void *\n+ngx_http_spdy_create_loc_conf(ngx_conf_t *cf)\n+{\n+    ngx_http_spdy_loc_conf_t  *slcf;\n+\n+    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_loc_conf_t));\n+    if (slcf == NULL) {\n+        return NULL;\n+    }\n+\n+    slcf->chunk_size = NGX_CONF_UNSET_SIZE;\n+\n+    return slcf;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n+{\n+    ngx_http_spdy_loc_conf_t *prev = parent;\n+    ngx_http_spdy_loc_conf_t *conf = child;\n+\n+    ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)\n+{\n+    size_t *sp = data;\n+\n+    if (*sp <= 2 * NGX_SPDY_STATE_BUFFER_SIZE) {\n+        return \"value is too small\";\n+    }\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data)\n+{\n+    size_t *sp = data;\n+\n+    if (*sp < NGX_MIN_POOL_SIZE) {\n+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                           \"the pool size must be no less than %uz\",\n+                           NGX_MIN_POOL_SIZE);\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    if (*sp % NGX_POOL_ALIGNMENT) {\n+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                           \"the pool size must be a multiple of %uz\",\n+                           NGX_POOL_ALIGNMENT);\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data)\n+{\n+    ngx_uint_t *np = data;\n+\n+    ngx_uint_t  mask;\n+\n+    mask = *np - 1;\n+\n+    if (*np == 0 || (*np & mask)) {\n+        return \"must be a power of two\";\n+    }\n+\n+    *np = mask;\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data)\n+{\n+    size_t *sp = data;\n+\n+    if (*sp == 0) {\n+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                           \"the spdy chunk size cannot be zero\");\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    if (*sp > NGX_SPDY_MAX_FRAME_SIZE) {\n+        *sp = NGX_SPDY_MAX_FRAME_SIZE;\n+    }\n+\n+    return NGX_CONF_OK;\n+}\ndiff -uNr a/src/http/ngx_http_spdy_module.h b/src/http/ngx_http_spdy_module.h\n--- a/src/http/ngx_http_spdy_module.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy_module.h\t2019-10-17 22:51:22.621255628 +0800\n@@ -0,0 +1,41 @@\n+\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#ifndef _NGX_HTTP_SPDY_MODULE_H_INCLUDED_\n+#define _NGX_HTTP_SPDY_MODULE_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+\n+\n+typedef struct {\n+    size_t                          recv_buffer_size;\n+    u_char                         *recv_buffer;\n+} ngx_http_spdy_main_conf_t;\n+\n+\n+typedef struct {\n+    size_t                          pool_size;\n+    ngx_uint_t                      concurrent_streams;\n+    ngx_uint_t                      streams_index_mask;\n+    ngx_msec_t                      recv_timeout;\n+    ngx_msec_t                      keepalive_timeout;\n+    ngx_int_t                       headers_comp;\n+} ngx_http_spdy_srv_conf_t;\n+\n+\n+typedef struct {\n+    size_t                          chunk_size;\n+} ngx_http_spdy_loc_conf_t;\n+\n+\n+extern ngx_module_t  ngx_http_spdy_module;\n+\n+\n+#endif /* _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ */\ndiff -uNr a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c\n--- a/src/http/ngx_http_upstream.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/ngx_http_upstream.c\t2019-10-17 22:51:22.623255645 +0800\n@@ -525,6 +525,12 @@\n         return;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        ngx_http_upstream_init_request(r);\n+        return;\n+    }\n+#endif\n \n     if (c->read->timer_set) {\n         ngx_del_timer(c->read);\n@@ -1347,6 +1353,11 @@\n         return;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        return;\n+    }\n+#endif\n \n #if (NGX_HAVE_KQUEUE)\n \ndiff -uNr a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c\n--- a/src/http/v2/ngx_http_v2.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.c\t2019-10-17 22:51:22.625255661 +0800\n@@ -270,6 +270,8 @@\n \n     h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n \n+    h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE;\n+\n     h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n \n     h2c->concurrent_pushes = h2scf->concurrent_pushes;\n@@ -2092,6 +2094,14 @@\n         case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:\n \n             h2c->table_update = 1;\n+\n+            if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+                h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE;\n+            } else {\n+                h2c->max_hpack_table_size = value;\n+            }\n+\n+            h2c->indicate_resize = 1;\n             break;\n \n         default:\ndiff -uNr a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c\n--- a/src/http/v2/ngx_http_v2_encode.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_encode.c\t2019-10-17 22:51:22.625255661 +0800\n@@ -10,7 +10,7 @@\n #include <ngx_http.h>\n \n \n-static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n+u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n     ngx_uint_t value);\n \n \n@@ -40,7 +40,7 @@\n }\n \n \n-static u_char *\n+u_char *\n ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)\n {\n     if (value < prefix) {\ndiff -uNr a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c\n--- a/src/http/v2/ngx_http_v2_filter_module.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_filter_module.c\t2019-10-17 22:51:22.626255669 +0800\n@@ -23,10 +23,53 @@\n #define ngx_http_v2_literal_size(h)                                           \\\n     (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)\n \n+#define ngx_http_v2_indexed(i)      (128 + (i))\n+#define ngx_http_v2_inc_indexed(i)  (64 + (i))\n+\n+#define NGX_HTTP_V2_ENCODE_RAW            0\n+#define NGX_HTTP_V2_ENCODE_HUFF           0x80\n+\n+#define NGX_HTTP_V2_AUTHORITY_INDEX       1\n+#define NGX_HTTP_V2_METHOD_GET_INDEX      2\n+#define NGX_HTTP_V2_PATH_INDEX            4\n+\n+#define NGX_HTTP_V2_SCHEME_HTTP_INDEX     6\n+#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX    7\n+\n+#define NGX_HTTP_V2_STATUS_INDEX          8\n+#define NGX_HTTP_V2_STATUS_200_INDEX      8\n+#define NGX_HTTP_V2_STATUS_204_INDEX      9\n+#define NGX_HTTP_V2_STATUS_206_INDEX      10\n+#define NGX_HTTP_V2_STATUS_304_INDEX      11\n+#define NGX_HTTP_V2_STATUS_400_INDEX      12\n+#define NGX_HTTP_V2_STATUS_404_INDEX      13\n+#define NGX_HTTP_V2_STATUS_500_INDEX      14\n+\n+#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16\n+#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17\n+#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX  28\n+#define NGX_HTTP_V2_CONTENT_TYPE_INDEX    31\n+#define NGX_HTTP_V2_DATE_INDEX            33\n+#define NGX_HTTP_V2_LAST_MODIFIED_INDEX   44\n+#define NGX_HTTP_V2_LOCATION_INDEX        46\n+#define NGX_HTTP_V2_SERVER_INDEX          54\n+#define NGX_HTTP_V2_USER_AGENT_INDEX      58\n+#define NGX_HTTP_V2_VARY_INDEX            59\n \n #define NGX_HTTP_V2_NO_TRAILERS           (ngx_http_v2_out_frame_t *) -1\n \n \n+static const struct {\n+    u_char        *name;\n+    u_char const   len;\n+} push_header[] = {\n+    { (u_char*)\":authority\"      , 10 },\n+    { (u_char*)\"accept-encoding\" , 15 },\n+    { (u_char*)\"accept-language\" , 15 },\n+    { (u_char*)\"user-agent\"      , 10 }\n+};\n+\n+\n typedef struct {\n     ngx_str_t      name;\n     u_char         index;\n@@ -155,11 +198,9 @@\n #endif\n \n     static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);\n-    static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];\n \n     static size_t nginx_ver_build_len =\n                                   ngx_http_v2_literal_size(NGINX_VER_BUILD);\n-    static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];\n \n     stream = r->stream;\n \n@@ -435,7 +476,7 @@\n     }\n \n     tmp = ngx_palloc(r->pool, tmp_len);\n-    pos = ngx_pnalloc(r->pool, len);\n+    pos = ngx_pnalloc(r->pool, len + 15 + 1);\n \n     if (pos == NULL || tmp == NULL) {\n         return NGX_ERROR;\n@@ -443,11 +484,16 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n     }\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n@@ -458,67 +504,28 @@\n         *pos++ = status;\n \n     } else {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);\n-        *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;\n-        pos = ngx_sprintf(pos, \"%03ui\", r->headers_out.status);\n+        ngx_sprintf(pos + 8, \"%O3ui\", r->headers_out.status);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\":status\",\n+                                       sizeof(\":status\") - 1, pos + 8, 3, tmp);\n     }\n \n     if (r->headers_out.server == NULL) {\n-\n         if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER);\n-\n-        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER_BUILD);\n-\n-        } else {\n-            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: nginx\\\"\");\n-        }\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);\n-\n-        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            if (nginx_ver[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,\n-                                            sizeof(NGINX_VER) - 1, tmp);\n-                nginx_ver_len = p - nginx_ver;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER);\n \n         } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            if (nginx_ver_build[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver_build,\n-                                            (u_char *) NGINX_VER_BUILD,\n-                                            sizeof(NGINX_VER_BUILD) - 1, tmp);\n-                nginx_ver_build_len = p - nginx_ver_build;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER_BUILD);\n \n         } else {\n-            pos = ngx_cpymem(pos, nginx, sizeof(nginx));\n+            pos = ngx_http_v2_write_header_str(\"server\", \"nginx\");\n         }\n     }\n \n     if (r->headers_out.date == NULL) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"date: %V\\\"\",\n-                       &ngx_cached_http_time);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);\n-        pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,\n-                                      ngx_cached_http_time.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"date\", ngx_cached_http_time);\n     }\n \n     if (r->headers_out.content_type.len) {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);\n-\n         if (r->headers_out.content_type_len == r->headers_out.content_type.len\n             && r->headers_out.charset.len)\n         {\n@@ -544,64 +551,36 @@\n             r->headers_out.content_type.data = p - len;\n         }\n \n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-type: %V\\\"\",\n-                       &r->headers_out.content_type);\n-\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,\n-                                      r->headers_out.content_type.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"content-type\",\n+                                           r->headers_out.content_type);\n     }\n \n     if (r->headers_out.content_length == NULL\n         && r->headers_out.content_length_n >= 0)\n     {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-length: %O\\\"\",\n-                       r->headers_out.content_length_n);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);\n-\n-        p = pos;\n-        pos = ngx_sprintf(pos + 1, \"%O\", r->headers_out.content_length_n);\n-        *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);\n+        p = ngx_sprintf(pos + 15, \"%O\", r->headers_out.content_length_n);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"content-length\",\n+                                       sizeof(\"content-length\") - 1, pos + 15,\n+                                       p - (pos + 15), tmp);\n     }\n \n     if (r->headers_out.last_modified == NULL\n         && r->headers_out.last_modified_time != -1)\n     {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);\n-\n-        ngx_http_time(pos, r->headers_out.last_modified_time);\n+        ngx_http_time(pos + 14, r->headers_out.last_modified_time);\n         len = sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1;\n-\n-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"last-modified: %*s\\\"\",\n-                       len, pos);\n-\n-        /*\n-         * Date will always be encoded using huffman in the temporary buffer,\n-         * so it's safe here to use src and dst pointing to the same address.\n-         */\n-        pos = ngx_http_v2_write_value(pos, pos, len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"last-modified\",\n+                                       sizeof(\"last-modified\") - 1, pos + 14,\n+                                       len, tmp);\n     }\n \n     if (r->headers_out.location && r->headers_out.location->value.len) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"location: %V\\\"\",\n-                       &r->headers_out.location->value);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,\n-                                      r->headers_out.location->value.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"location\", r->headers_out.location->value);\n     }\n \n #if (NGX_HTTP_GZIP)\n     if (r->gzip_vary) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"vary: Accept-Encoding\\\"\");\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);\n-        pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));\n+        pos = ngx_http_v2_write_header_str(\"vary\", \"Accept-Encoding\");\n     }\n #endif\n \n@@ -624,23 +603,10 @@\n             continue;\n         }\n \n-#if (NGX_DEBUG)\n-        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n-            ngx_strlow(tmp, header[i].key.data, header[i].key.len);\n-\n-            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"%*s: %V\\\"\",\n-                           header[i].key.len, tmp, &header[i].value);\n-        }\n-#endif\n-\n-        *pos++ = 0;\n-\n-        pos = ngx_http_v2_write_name(pos, header[i].key.data,\n-                                     header[i].key.len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data,\n+                                       header[i].key.len, header[i].value.data,\n+                                       header[i].value.len, tmp);\n \n-        pos = ngx_http_v2_write_value(pos, header[i].value.data,\n-                                      header[i].value.len, tmp);\n     }\n \n     fin = r->header_only\n@@ -998,6 +964,7 @@\n \n     for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n         len += binary[i].len;\n+        len += push_header[i].len + 1;\n     }\n \n     pos = ngx_pnalloc(r->pool, len);\n@@ -1007,12 +974,17 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n-    }\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n+     }\n \n     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":method: GET\\\"\");\n@@ -1022,8 +994,7 @@\n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":path: %V\\\"\", path);\n \n-    *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n-    pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);\n+    pos = ngx_http_v2_write_header_pot(\":path\", path);\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":scheme: %V\\\"\", &r->schema);\n@@ -1048,11 +1019,15 @@\n             continue;\n         }\n \n+        value = &(*h)->value;\n+\n         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                        \"http2 push header: \\\"%V: %V\\\"\",\n                        &ph[i].name, &(*h)->value);\n \n-        pos = ngx_cpymem(pos, binary[i].data, binary[i].len);\n+        pos = ngx_http_v2_write_header(h2c, pos,\n+                  push_header[i].name, push_header[i].len, value->data, value->len,\n+                  tmp);\n     }\n \n     frame = ngx_http_v2_create_push_frame(r, start, pos);\ndiff -uNr a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h\n--- a/src/http/v2/ngx_http_v2.h\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.h\t2019-10-17 22:51:22.627255677 +0800\n@@ -52,6 +52,14 @@\n #define NGX_HTTP_V2_MAX_WINDOW           ((1U << 31) - 1)\n #define NGX_HTTP_V2_DEFAULT_WINDOW       65535\n \n+#define HPACK_ENC_HTABLE_SZ              128 /* better to keep a PoT < 64k */\n+#define HPACK_ENC_HTABLE_ENTRIES         ((HPACK_ENC_HTABLE_SZ * 100) / 128)\n+#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ     10  /* 10 is sufficient for most */\n+#define HPACK_ENC_MAX_ENTRY              512 /* longest header size to match */\n+\n+#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE     4096\n+#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE         16384 /* < 64k */\n+\n #define NGX_HTTP_V2_DEFAULT_WEIGHT       16\n \n \n@@ -115,6 +123,46 @@\n } ngx_http_v2_hpack_t;\n \n \n+#if (NGX_HTTP_V2_HPACK_ENC)\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen, vlen;\n+    uint16_t                         size;\n+    uint16_t                         next;\n+} ngx_http_v2_hpack_enc_entry_t;\n+\n+\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen;\n+} ngx_http_v2_hpack_name_entry_t;\n+\n+\n+typedef struct {\n+    size_t                           size;    /* size as defined in RFC 7541 */\n+    uint32_t                         top;     /* the last entry */\n+    uint32_t                         pos;\n+    uint16_t                         n_elems; /* number of elements */\n+    uint16_t                         base;    /* index of the oldest entry */\n+    uint16_t                         last;    /* index of the newest entry */\n+\n+    /* hash table for dynamic entries, instead using a generic hash table,\n+       which would be too slow to process a significant amount of headers,\n+       this table is not determenistic, and might ocasionally fail to insert\n+       a value, at the cost of slightly worse compression, but significantly\n+       faster performance */\n+    ngx_http_v2_hpack_enc_entry_t    htable[HPACK_ENC_HTABLE_SZ];\n+    ngx_http_v2_hpack_name_entry_t   heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ];\n+    u_char                           storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE +\n+                                             HPACK_ENC_MAX_ENTRY];\n+} ngx_http_v2_hpack_enc_t;\n+#endif\n+\n+\n struct ngx_http_v2_connection_s {\n     ngx_connection_t                *connection;\n     ngx_http_connection_t           *http_connection;\n@@ -136,6 +184,8 @@\n \n     size_t                           frame_size;\n \n+    size_t                           max_hpack_table_size;\n+\n     ngx_queue_t                      waiting;\n \n     ngx_http_v2_state_t              state;\n@@ -163,6 +213,11 @@\n     unsigned                         blocked:1;\n     unsigned                         goaway:1;\n     unsigned                         push_disabled:1;\n+    unsigned                         indicate_resize:1;\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+    ngx_http_v2_hpack_enc_t          hpack_enc;\n+#endif\n };\n \n \n@@ -206,6 +261,8 @@\n \n     ngx_array_t                     *cookies;\n \n+    size_t                           header_limit;\n+\n     ngx_pool_t                      *pool;\n \n     unsigned                         waiting:1;\n@@ -418,4 +475,35 @@\n     u_char *tmp, ngx_uint_t lower);\n \n \n+u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,\n+    u_char *tmp, ngx_uint_t lower);\n+\n+u_char *\n+ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value);\n+\n+#define ngx_http_v2_write_name(dst, src, len, tmp)                            \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 1)\n+#define ngx_http_v2_write_value(dst, src, len, tmp)                           \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 0)\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+    u_char *key, size_t key_len, u_char *value, size_t value_len,\n+    u_char *tmp);\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c);\n+\n+#define ngx_http_v2_write_header_str(key, value)                        \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    (u_char *) value, sizeof(value) - 1, tmp);\n+\n+#define ngx_http_v2_write_header_tbl(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val.data, val.len, tmp);\n+\n+#define ngx_http_v2_write_header_pot(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val->data, val->len, tmp);\n+\n #endif /* _NGX_HTTP_V2_H_INCLUDED_ */\ndiff -uNr a/src/http/v2/ngx_http_v2_module.c b/src/http/v2/ngx_http_v2_module.c\n--- a/src/http/v2/ngx_http_v2_module.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_module.c\t2019-10-17 22:51:22.634255735 +0800\n@@ -36,8 +36,6 @@\n static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post,\n     void *data);\n static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data);\n-static char *ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd,\n-    void *conf);\n \n \n static ngx_conf_post_t  ngx_http_v2_recv_buffer_size_post =\n@@ -152,62 +150,6 @@\n       0,\n       NULL },\n \n-    { ngx_string(\"spdy_recv_buffer_size\"),\n-      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_MAIN_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_pool_size\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_max_concurrent_streams\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_streams_index_size\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_recv_timeout\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_keepalive_timeout\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_headers_comp\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_chunk_size\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_LOC_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n       ngx_null_command\n };\n \n@@ -597,14 +539,3 @@\n \n     return NGX_CONF_OK;\n }\n-\n-\n-static char *\n-ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n-{\n-    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n-                       \"invalid directive \\\"%V\\\": ngx_http_spdy_module \"\n-                       \"was superseded by ngx_http_v2_module\", &cmd->name);\n-\n-    return NGX_CONF_OK;\n-}\ndiff -uNr a/src/http/v2/ngx_http_v2_table.c b/src/http/v2/ngx_http_v2_table.c\n--- a/src/http/v2/ngx_http_v2_table.c\t2019-09-24 23:08:48.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_table.c\t2019-10-17 22:51:22.637255759 +0800\n@@ -361,3 +361,434 @@\n \n     return NGX_OK;\n }\n+\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len);\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len);\n+\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c)\n+{\n+    ngx_http_v2_hpack_enc_entry_t  *table;\n+    uint64_t                        idx;\n+\n+    table = h2c->hpack_enc.htable;\n+\n+    while (h2c->hpack_enc.size > h2c->max_hpack_table_size) {\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+}\n+\n+\n+/* checks if a header is in the hpack table - if so returns the table entry,\n+   otherwise encodes and inserts into the table and returns 0,\n+   if failed to insert into table, returns -1 */\n+static ngx_int_t\n+ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c,\n+    size_t key_len, size_t val_len, uint8_t *key, uint8_t *val,\n+    ngx_int_t *header_idx)\n+{\n+    uint64_t  hash_val, key_hash, idx, lru;\n+    int       i;\n+    size_t    size = key_len + val_len + 32;\n+    uint8_t  *storage = h2c->hpack_enc.storage;\n+\n+    ngx_http_v2_hpack_enc_entry_t   *table;\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+\n+    *header_idx = NGX_ERROR;\n+    /* step 1: compute the hash value of header */\n+    if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) {\n+        return NGX_ERROR;\n+    }\n+\n+    key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234);\n+    hash_val = ngx_murmur_hash2_64(val, val_len, key_hash);\n+\n+    if (hash_val == 0) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* step 2: check if full header in the table */\n+    idx = hash_val;\n+    i = -1;\n+    while (idx) {\n+         /* at most 8 locations are checked, but most will be done in 1 or 2 */\n+        table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ];\n+        if (table->hash_val == hash_val\n+            && table->klen == key_len\n+            && table->vlen == val_len\n+            && ngx_memcmp(key, storage + table->pos, key_len) == 0\n+            && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0)\n+        {\n+            return (h2c->hpack_enc.top - table->index) + 61;\n+        }\n+\n+        if (table->hash_val == 0 && i == -1) {\n+            i = idx % HPACK_ENC_HTABLE_SZ;\n+            break;\n+        }\n+\n+        idx >>= 8;\n+    }\n+\n+    /* step 3: check if key is in one of the tables */\n+    *header_idx = hpack_get_static_index(h2c, key, key_len);\n+\n+    if (i == -1) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (*header_idx == NGX_ERROR) {\n+        *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len);\n+    }\n+\n+    /* step 4: store the new entry */\n+    table =  h2c->hpack_enc.htable;\n+\n+    if (h2c->hpack_enc.top == 0xffffffff) {\n+        /* just to be on the safe side, avoid overflow */\n+        ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t));\n+    }\n+\n+    while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size)\n+           || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) {\n+        /* make space for the new entry first */\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+\n+    table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val,\n+                                               .index = h2c->hpack_enc.top,\n+                                               .pos = h2c->hpack_enc.pos,\n+                                               .klen = key_len,\n+                                               .vlen = val_len,\n+                                               .size = size,\n+                                               .next = 0};\n+\n+    table[h2c->hpack_enc.last].next = i;\n+    if (h2c->hpack_enc.n_elems == 0) {\n+        h2c->hpack_enc.base = i;\n+    }\n+\n+    h2c->hpack_enc.last = i;\n+    h2c->hpack_enc.top++;\n+    h2c->hpack_enc.size += size;\n+    h2c->hpack_enc.n_elems++;\n+\n+    /* update header name lookup */\n+    if (*header_idx == NGX_ERROR ) {\n+        lru = h2c->hpack_enc.top;\n+\n+        for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+\n+            name = &h2c->hpack_enc.heads[i];\n+\n+            if ( name->hash_val == 0 || (name->hash_val == key_hash\n+                && ngx_memcmp(storage + name->pos, key, key_len) == 0) )\n+            {\n+                name->hash_val = key_hash;\n+                name->pos = h2c->hpack_enc.pos;\n+                name->index = h2c->hpack_enc.top - 1;\n+                break;\n+            }\n+\n+            if (lru > name->index) {\n+                lru = name->index;\n+                idx = i;\n+            }\n+        }\n+\n+        if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) {\n+            name = &h2c->hpack_enc.heads[idx];\n+            name->hash_val = hash_val;\n+            name->pos = h2c->hpack_enc.pos;\n+            name->index = h2c->hpack_enc.top - 1;\n+        }\n+    }\n+\n+    ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len);\n+    ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len);\n+\n+    h2c->hpack_enc.pos += size;\n+    if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+        h2c->hpack_enc.pos = 0;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_int_t idx, header_idx;\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    /* attempt to find the value in the dynamic table */\n+    idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value,\n+                                           &header_idx);\n+\n+    if (idx > 0) {\n+        /* positive index indicates success */\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                       \"http2 hpack encode: Indexed Header Field: %ud\", idx);\n+\n+        *pos = 128;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx);\n+\n+    } else {\n+\n+        if (header_idx == NGX_ERROR) { /* if key is not present */\n+\n+            if (idx == NGX_ERROR) {    /* if header was not added */\n+                *pos++ = 0;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — New Name\");\n+            } else {                   /* if header was added */\n+                *pos++ = 64;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — New Name\");\n+            }\n+\n+            pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+\n+        } else {                       /* if key is present */\n+\n+            if (idx == NGX_ERROR) {\n+                *pos = 0;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — Indexed Name: %ud\", header_idx);\n+            } else {\n+                *pos = 64;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — Indexed Name: %ud\", header_idx);\n+            }\n+        }\n+\n+        pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+    }\n+\n+    return pos;\n+}\n+\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len)\n+{\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+    int                              i;\n+\n+    for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+        name = &h2c->hpack_enc.heads[i];\n+\n+        if (name->hash_val == key_hash\n+            && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0)\n+        {\n+            if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) {\n+                return (h2c->hpack_enc.top - name->index) + 61;\n+            }\n+            break;\n+        }\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+\n+/* decide if a given header is present in the static dictionary, this could be\n+   done in several ways, but it seems the fastest one is \"exhaustive\" search */\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len)\n+{\n+    /* the static dictionary of response only headers,\n+       although response headers can be put by origin,\n+       that would be rare */\n+    static const struct {\n+        u_char         len;\n+        const u_char   val[28];\n+        u_char         idx;\n+    } server_headers[] = {\n+        { 3, \"age\",                         21},//0\n+        { 3, \"via\",                         60},\n+        { 4, \"date\",                        33},//2\n+        { 4, \"etag\",                        34},\n+        { 4, \"link\",                        45},\n+        { 4, \"vary\",                        59},\n+        { 5, \"allow\",                       22},//6\n+        { 6, \"server\",                      54},//7\n+        { 7, \"expires\",                     36},//8\n+        { 7, \"refresh\",                     52},\n+        { 8, \"location\",                    46},//10\n+        {10, \"set-cookie\",                  55},//11\n+        {11, \"retry-after\",                 53},//12\n+        {12, \"content-type\",                31},//13\n+        {13, \"content-range\",               30},//14\n+        {13, \"accept-ranges\",               18},\n+        {13, \"cache-control\",               24},\n+        {13, \"last-modified\",               44},\n+        {14, \"content-length\",              28},//18\n+        {16, \"content-encoding\",            26},//19\n+        {16, \"content-language\",            27},\n+        {16, \"content-location\",            29},\n+        {16, \"www-authenticate\",            61},\n+        {17, \"transfer-encoding\",           57},//23\n+        {18, \"proxy-authenticate\",          48},//24\n+        {19, \"content-disposition\",         25},//25\n+        {25, \"strict-transport-security\",   56},//26\n+        {27, \"access-control-allow-origin\", 20},//27\n+        {99, \"\",                            99},\n+    }, *header;\n+\n+    /* for a given length, where to start the search\n+       since minimal length is 3, the table has a -3\n+       offset */\n+    static const int8_t start_at[] = {\n+        [3-3]  = 0,\n+        [4-3]  = 2,\n+        [5-3]  = 6,\n+        [6-3]  = 7,\n+        [7-3]  = 8,\n+        [8-3]  = 10,\n+        [9-3]  = -1,\n+        [10-3] = 11,\n+        [11-3] = 12,\n+        [12-3] = 13,\n+        [13-3] = 14,\n+        [14-3] = 18,\n+        [15-3] = -1,\n+        [16-3] = 19,\n+        [17-3] = 23,\n+        [18-3] = 24,\n+        [19-3] = 25,\n+        [20-3] = -1,\n+        [21-3] = -1,\n+        [22-3] = -1,\n+        [23-3] = -1,\n+        [24-3] = -1,\n+        [25-3] = 26,\n+        [26-3] = -1,\n+        [27-3] = 27,\n+    };\n+\n+    uint64_t pref;\n+    size_t   save_len = len, i;\n+    int8_t   start;\n+\n+    /* early exit for out of bounds lengths */\n+    if (len < 3 || len > 27) {\n+        return NGX_ERROR;\n+    }\n+\n+    start = start_at[len - 3];\n+    if (start == -1) {\n+        /* exit for non existent lengths */\n+        return NGX_ERROR;\n+    }\n+\n+    header = &server_headers[start_at[len - 3]];\n+\n+    /* load first 8 bytes of key, for fast comparison */\n+    if (len < 8) {\n+        pref = 0;\n+        if (len >= 4) {\n+            pref = *(uint32_t *)(val + len - 4) | 0x20202020;\n+            len -= 4;\n+        }\n+        while (len > 0) { /* 3 iterations at most */\n+            pref = (pref << 8) ^ (val[len - 1] | 0x20);\n+            len--;\n+        }\n+    } else {\n+        pref = *(uint64_t *)val | 0x2020202020202020;\n+        len -= 8;\n+    }\n+\n+    /* iterate over headers with the right length */\n+    while (header->len == save_len) {\n+        /* quickly compare the first 8 bytes, most tests will end here */\n+        if (pref != *(uint64_t *) header->val) {\n+            header++;\n+            continue;\n+        }\n+\n+        if (len == 0) {\n+            /* len == 0, indicates prefix held the entire key */\n+            return header->idx;\n+        }\n+        /* for longer keys compare the rest */\n+        i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */\n+\n+        while (i + 8 <= save_len) { /* 3 iterations at most */\n+            if ( *(uint64_t *)&header->val[i]\n+                 != (*(uint64_t *) &val[i]| 0x2020202020202020) )\n+            {\n+                header++;\n+                i = 0;\n+                break;\n+            }\n+            i += 8;\n+        }\n+\n+        if (i == 0) {\n+            continue;\n+        }\n+\n+        /* found the corresponding entry in the static dictionary */\n+        return header->idx;\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+#else\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    *pos++ = 64;\n+    pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+    pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+\n+    return pos;\n+}\n+\n+#endif\n"
  },
  {
    "path": "nginx_with_spdy_quic.patch",
    "content": "Add SPDY Support.\nAdd HTTP3(QUIC) Support.\nAdd HTTP2 HPACK Encoding Support.\nAdd Dynamic TLS Record support.\n\nUsing: patch -p1 < nginx.patch\n\n\ndiff -uNr a/auto/lib/conf b/auto/lib/conf\n--- a/auto/lib/conf\t2019-12-24 23:00:09.000000000 +0800\n+++ b/auto/lib/conf\t2020-01-02 21:25:20.000000000 +0800\n@@ -25,6 +25,10 @@\n     . auto/lib/openssl/conf\n fi\n \n+if [ $USE_QUICHE = YES ]; then\n+    . auto/lib/quiche/conf\n+fi\n+\n if [ $USE_ZLIB = YES ]; then\n     . auto/lib/zlib/conf\n fi\ndiff -uNr a/auto/lib/make b/auto/lib/make\n--- a/auto/lib/make\t2019-12-24 23:00:09.000000000 +0800\n+++ b/auto/lib/make\t2020-01-02 21:25:20.000000000 +0800\n@@ -11,6 +11,10 @@\n     . auto/lib/openssl/make\n fi\n \n+if [ $QUICHE != NONE -a $QUICHE != NO -a $QUICHE != YES ]; then\n+    . auto/lib/quiche/make\n+fi\n+\n if [ $ZLIB != NONE -a $ZLIB != NO -a $ZLIB != YES ]; then\n     . auto/lib/zlib/make\n fi\ndiff -uNr a/auto/lib/openssl/make b/auto/lib/openssl/make\n--- a/auto/lib/openssl/make\t2019-12-24 23:00:09.000000000 +0800\n+++ b/auto/lib/openssl/make\t2020-01-02 21:25:20.000000000 +0800\n@@ -49,11 +49,13 @@\n         cat << END                                            >> $NGX_MAKEFILE\n \n $OPENSSL/.openssl/include/openssl/ssl.h:\t$NGX_MAKEFILE\n-\tcd $OPENSSL \\\\\n-\t&& if [ -f Makefile ]; then \\$(MAKE) clean; fi \\\\\n-\t&& ./config --prefix=$ngx_prefix no-shared no-threads $OPENSSL_OPT \\\\\n-\t&& \\$(MAKE) \\\\\n-\t&& \\$(MAKE) install_sw LIBDIR=lib\n+\tmkdir -p $OPENSSL/build $OPENSSL/.openssl/lib $OPENSSL/.openssl/include/openssl \\\\\n+\t&& cd $OPENSSL/build \\\\\n+\t&& cmake -DCMAKE_C_FLAGS=\"$OPENSSL_OPT\" -DCMAKE_CXX_FLAGS=\"$OPENSSL_OPT\" .. \\\\\n+\t&& \\$(MAKE) VERBOSE=1 \\\\\n+\t&& cd .. \\\\\n+\t&& cp -r include/openssl/*.h .openssl/include/openssl \\\\\n+\t&& cp build/ssl/libssl.a build/crypto/libcrypto.a .openssl/lib\n \n END\n \ndiff -uNr a/auto/lib/quiche/conf b/auto/lib/quiche/conf\n--- a/auto/lib/quiche/conf\t1970-01-01 08:00:00.000000000 +0800\n+++ b/auto/lib/quiche/conf\t2020-01-02 21:25:20.000000000 +0800\n@@ -0,0 +1,19 @@\n+\n+# Copyright (C) Cloudflare, Inc.\n+\n+\n+if [ $QUICHE != NONE ]; then\n+\n+    have=NGX_QUIC . auto/have\n+\n+    QUICHE_BUILD_TARGET=\"release\"\n+\n+    if [ $NGX_DEBUG = YES ]; then\n+        QUICHE_BUILD_TARGET=\"debug\"\n+    fi\n+\n+    CORE_INCS=\"$CORE_INCS $QUICHE/include\"\n+    CORE_DEPS=\"$CORE_DEPS $QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a\"\n+    CORE_LIBS=\"$CORE_LIBS $QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a $NGX_LIBPTHREAD\"\n+\n+fi\ndiff -uNr a/auto/lib/quiche/make b/auto/lib/quiche/make\n--- a/auto/lib/quiche/make\t1970-01-01 08:00:00.000000000 +0800\n+++ b/auto/lib/quiche/make\t2020-01-02 21:25:20.000000000 +0800\n@@ -0,0 +1,22 @@\n+\n+# Copyright (C) Cloudflare, Inc.\n+\n+\n+# Default is release build\n+QUICHE_BUILD_FLAGS=\"--release --no-default-features\"\n+QUICHE_BUILD_TARGET=\"release\"\n+\n+if [ $NGX_DEBUG = YES ]; then\n+    QUICHE_BUILD_FLAGS=\"--no-default-features\"\n+    QUICHE_BUILD_TARGET=\"debug\"\n+fi\n+\n+\n+cat << END                                                    >> $NGX_MAKEFILE\n+\n+$QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a: \\\\\n+\t\t$OPENSSL/.openssl/include/openssl/ssl.h \\\\\n+\t\t$NGX_MAKEFILE\n+\tcd $QUICHE && cargo build $QUICHE_BUILD_FLAGS\n+\n+END\ndiff -uNr a/auto/make b/auto/make\n--- a/auto/make\t2019-12-24 23:00:09.000000000 +0800\n+++ b/auto/make\t2020-01-02 21:25:20.000000000 +0800\n@@ -7,7 +7,8 @@\n \n mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \\\n          $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \\\n-         $NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/modules \\\n+         $NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/v3 \\\n+         $NGX_OBJS/src/http/modules \\\n          $NGX_OBJS/src/http/modules/perl \\\n          $NGX_OBJS/src/mail \\\n          $NGX_OBJS/src/stream \\\ndiff -uNr a/auto/modules b/auto/modules\n--- a/auto/modules\t2019-12-24 23:00:09.000000000 +0800\n+++ b/auto/modules\t2020-01-03 10:50:12.000000000 +0800\n@@ -118,7 +118,9 @@\n     #     ngx_http_write_filter\n     #     ngx_http_header_filter\n     #     ngx_http_chunked_filter\n+    #     ngx_http_spdy_filter\n     #     ngx_http_v2_filter\n+    #     ngx_http_v3_filter\n     #     ngx_http_range_header_filter\n     #     ngx_http_gzip_filter\n     #     ngx_http_postpone_filter\n@@ -150,7 +152,9 @@\n                       ngx_http_write_filter_module \\\n                       ngx_http_header_filter_module \\\n                       ngx_http_chunked_filter_module \\\n+                      ngx_http_spdy_filter_module \\\n                       ngx_http_v2_filter_module \\\n+                      ngx_http_v3_filter_module \\\n                       ngx_http_range_header_filter_module \\\n                       ngx_http_gzip_filter_module \\\n                       ngx_http_postpone_filter_module \\\n@@ -201,6 +205,19 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_SPDY = YES ]; then\n+        have=NGX_HTTP_SPDY . auto/have\n+        USE_ZLIB=YES\n+        ngx_module_name=ngx_http_spdy_filter_module\n+        ngx_module_incs=\n+        ngx_module_deps=\n+        ngx_module_srcs=src/http/ngx_http_spdy_filter_module.c\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_SPDY\n+\n+        . auto/module\n+    fi\n+\n     if [ $HTTP_V2 = YES ]; then\n         ngx_module_name=ngx_http_v2_filter_module\n         ngx_module_incs=\n@@ -212,6 +229,17 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_V3 = YES ]; then\n+        ngx_module_name=ngx_http_v3_filter_module\n+        ngx_module_incs=\n+        ngx_module_deps=\n+        ngx_module_srcs=src/http/v3/ngx_http_v3_filter_module.c\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_V3\n+\n+        . auto/module\n+    fi\n+\n     if :; then\n         ngx_module_name=ngx_http_range_header_filter_module\n         ngx_module_incs=\n@@ -403,6 +431,19 @@\n \n     ngx_module_type=HTTP\n \n+    if [ $HTTP_SPDY = YES ]; then\n+        have=NGX_HTTP_SPDY . auto/have\n+        ngx_module_name=ngx_http_spdy_module\n+        ngx_module_incs=src/http\n+        ngx_module_deps=\"src/http/ngx_http_spdy.h src/http/ngx_http_spdy_module.h\"\n+        ngx_module_srcs=\"src/http/ngx_http_spdy.c \\\n+                         src/http/ngx_http_spdy_module.c\"\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_SPDY\n+\n+        . auto/module\n+    fi\n+\n     if [ $HTTP_V2 = YES ]; then\n         have=NGX_HTTP_V2 . auto/have\n         have=NGX_HTTP_HEADERS . auto/have\n@@ -423,6 +464,28 @@\n         . auto/module\n     fi\n \n+    if [ $HTTP_V3 = YES ]; then\n+        USE_QUICHE=YES\n+        USE_OPENSSL=YES\n+        have=NGX_HTTP_V3 . auto/have\n+        have=NGX_HTTP_HEADERS . auto/have\n+\n+        ngx_module_name=ngx_http_v3_module\n+        ngx_module_incs=src/http/v3\n+        ngx_module_deps=\"src/http/v3/ngx_http_v3.h \\\n+                         src/http/v3/ngx_http_v3_module.h\"\n+        ngx_module_srcs=\"src/http/v3/ngx_http_v3.c \\\n+                         src/http/v3/ngx_http_v3_module.c\"\n+        ngx_module_libs=\n+        ngx_module_link=$HTTP_V3\n+\n+        . auto/module\n+    fi\n+\n+    if [ $HTTP_V2_HPACK_ENC = YES ]; then\n+        have=NGX_HTTP_V2_HPACK_ENC . auto/have\n+    fi\n+\n     if :; then\n         ngx_module_name=ngx_http_static_module\n         ngx_module_incs=\n@@ -1251,6 +1314,19 @@\n \n     . auto/module\n fi\n+\n+\n+if [ $USE_QUICHE = YES ]; then\n+    ngx_module_type=CORE\n+    ngx_module_name=ngx_quic_module\n+    ngx_module_incs=\n+    ngx_module_deps=src/event/ngx_event_quic.h\n+    ngx_module_srcs=src/event/ngx_event_quic.c\n+    ngx_module_libs=\n+    ngx_module_link=YES\n+\n+    . auto/module\n+fi\n \n \n if [ $USE_PCRE = YES ]; then\ndiff -uNr a/auto/options b/auto/options\n--- a/auto/options\t2019-12-24 23:00:09.000000000 +0800\n+++ b/auto/options\t2020-01-03 10:51:20.000000000 +0800\n@@ -58,7 +58,10 @@\n HTTP_CHARSET=YES\n HTTP_GZIP=YES\n HTTP_SSL=NO\n+HTTP_SPDY=NO\n HTTP_V2=NO\n+HTTP_V2_HPACK_ENC=NO\n+HTTP_V3=NO\n HTTP_SSI=YES\n HTTP_REALIP=NO\n HTTP_XSLT=NO\n@@ -147,6 +150,9 @@\n USE_OPENSSL=NO\n OPENSSL=NONE\n \n+USE_QUICHE=NO\n+QUICHE=NONE\n+\n USE_ZLIB=NO\n ZLIB=NONE\n ZLIB_OPT=\n@@ -223,7 +229,10 @@\n         --http-scgi-temp-path=*)         NGX_HTTP_SCGI_TEMP_PATH=\"$value\" ;;\n \n         --with-http_ssl_module)          HTTP_SSL=YES               ;;\n+        --with-http_spdy_module)         HTTP_SPDY=YES              ;;\n         --with-http_v2_module)           HTTP_V2=YES                ;;\n+        --with-http_v2_hpack_enc)        HTTP_V2_HPACK_ENC=YES      ;;\n+        --with-http_v3_module)           HTTP_V3=YES                ;;\n         --with-http_realip_module)       HTTP_REALIP=YES            ;;\n         --with-http_addition_module)     HTTP_ADDITION=YES          ;;\n         --with-http_xslt_module)         HTTP_XSLT=YES              ;;\n@@ -357,6 +366,9 @@\n         --with-openssl=*)                OPENSSL=\"$value\"           ;;\n         --with-openssl-opt=*)            OPENSSL_OPT=\"$value\"       ;;\n \n+        --with-quiche=*)                 QUICHE=\"$value\"           ;;\n+        --with-quiche-opt=*)             QUICHE_OPT=\"$value\"       ;;\n+\n         --with-md5=*)\n             NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n $0: warning: the \\\"--with-md5\\\" option is deprecated\"\n@@ -438,7 +450,10 @@\n   --with-file-aio                    enable file AIO support\n \n   --with-http_ssl_module             enable ngx_http_ssl_module\n+  --with-http_spdy_module            enable ngx_http_spdy_module\n   --with-http_v2_module              enable ngx_http_v2_module\n+  --with-http_v2_hpack_enc           enable ngx_http_v2_hpack_enc\n+  --with-http_v3_module              enable ngx_http_v3_module\n   --with-http_realip_module          enable ngx_http_realip_module\n   --with-http_addition_module        enable ngx_http_addition_module\n   --with-http_xslt_module            enable ngx_http_xslt_module\ndiff -uNr a/src/core/ngx_connection.h b/src/core/ngx_connection.h\n--- a/src/core/ngx_connection.h\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/core/ngx_connection.h\t2020-01-03 10:51:59.000000000 +0800\n@@ -79,6 +79,9 @@\n     unsigned            deferred_accept:1;\n     unsigned            delete_deferred:1;\n     unsigned            add_deferred:1;\n+#if (NGX_QUIC)\n+    unsigned            quic:1;\n+#endif\n #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n     char               *accept_filter;\n #endif\n@@ -119,6 +122,7 @@\n #define NGX_LOWLEVEL_BUFFERED  0x0f\n #define NGX_SSL_BUFFERED       0x01\n #define NGX_HTTP_V2_BUFFERED   0x02\n+#define NGX_SPDY_BUFFERED      0x04\n \n \n struct ngx_connection_s {\n@@ -155,6 +159,10 @@\n \n     ngx_udp_connection_t  *udp;\n \n+#if (NGX_QUIC)\n+    ngx_quic_connection_t *quic;\n+#endif\n+\n     struct sockaddr    *local_sockaddr;\n     socklen_t           local_socklen;\n \ndiff -uNr a/src/core/ngx_core.h b/src/core/ngx_core.h\n--- a/src/core/ngx_core.h\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/core/ngx_core.h\t2020-01-02 21:25:20.000000000 +0800\n@@ -83,6 +83,9 @@\n #if (NGX_OPENSSL)\n #include <ngx_event_openssl.h>\n #endif\n+#if (NGX_QUIC)\n+#include <ngx_event_quic.h>\n+#endif\n #include <ngx_process_cycle.h>\n #include <ngx_conf_file.h>\n #include <ngx_module.h>\ndiff -uNr a/src/core/ngx_murmurhash.c b/src/core/ngx_murmurhash.c\n--- a/src/core/ngx_murmurhash.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/core/ngx_murmurhash.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -50,3 +50,63 @@\n \n     return h;\n }\n+\n+\n+uint64_t\n+ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed)\n+{\n+    uint64_t  h, k;\n+\n+    h = seed ^ len;\n+\n+    while (len >= 8) {\n+        k  = data[0];\n+        k |= data[1] << 8;\n+        k |= data[2] << 16;\n+        k |= data[3] << 24;\n+        k |= (uint64_t)data[4] << 32;\n+        k |= (uint64_t)data[5] << 40;\n+        k |= (uint64_t)data[6] << 48;\n+        k |= (uint64_t)data[7] << 56;\n+\n+        k *= 0xc6a4a7935bd1e995ull;\n+        k ^= k >> 47;\n+        k *= 0xc6a4a7935bd1e995ull;\n+\n+        h ^= k;\n+        h *= 0xc6a4a7935bd1e995ull;\n+\n+        data += 8;\n+        len -= 8;\n+    }\n+\n+    switch (len) {\n+    case 7:\n+        h ^= (uint64_t)data[6] << 48;\n+        /* fall through */\n+    case 6:\n+        h ^= (uint64_t)data[5] << 40;\n+        /* fall through */\n+    case 5:\n+        h ^= (uint64_t)data[4] << 32;\n+        /* fall through */\n+    case 4:\n+        h ^= data[3] << 24;\n+        /* fall through */\n+    case 3:\n+        h ^= data[2] << 16;\n+        /* fall through */\n+    case 2:\n+        h ^= data[1] << 8;\n+        /* fall through */\n+    case 1:\n+        h ^= data[0];\n+        h *= 0xc6a4a7935bd1e995ull;\n+    }\n+\n+    h ^= h >> 47;\n+    h *= 0xc6a4a7935bd1e995ull;\n+    h ^= h >> 47;\n+\n+    return h;\n+}\ndiff -uNr a/src/core/ngx_murmurhash.h b/src/core/ngx_murmurhash.h\n--- a/src/core/ngx_murmurhash.h\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/core/ngx_murmurhash.h\t2020-01-02 21:25:20.000000000 +0800\n@@ -15,5 +15,7 @@\n \n uint32_t ngx_murmur_hash2(u_char *data, size_t len);\n \n+uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed);\n+\n \n #endif /* _NGX_MURMURHASH_H_INCLUDED_ */\ndiff -uNr a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c\n--- a/src/event/ngx_event_openssl.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/event/ngx_event_openssl.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -1507,6 +1507,7 @@\n \n     sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);\n     sc->buffer_size = ssl->buffer_size;\n+    sc->dyn_rec = ssl->dyn_rec;\n \n     sc->session_ctx = ssl->ctx;\n \n@@ -2400,6 +2401,41 @@\n \n     for ( ;; ) {\n \n+        /* Dynamic record resizing:\n+           We want the initial records to fit into one TCP segment\n+           so we don't get TCP HoL blocking due to TCP Slow Start.\n+           A connection always starts with small records, but after\n+           a given amount of records sent, we make the records larger\n+           to reduce header overhead.\n+           After a connection has idled for a given timeout, begin\n+           the process from the start. The actual parameters are\n+           configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */\n+\n+        if (c->ssl->dyn_rec.timeout > 0 ) {\n+\n+            if (ngx_current_msec - c->ssl->dyn_rec_last_write >\n+                c->ssl->dyn_rec.timeout)\n+            {\n+                buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                c->ssl->dyn_rec_records_sent = 0;\n+\n+            } else {\n+                if (c->ssl->dyn_rec_records_sent >\n+                    c->ssl->dyn_rec.threshold * 2)\n+                {\n+                    buf->end = buf->start + c->ssl->buffer_size;\n+\n+                } else if (c->ssl->dyn_rec_records_sent >\n+                           c->ssl->dyn_rec.threshold)\n+                {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_hi;\n+\n+                } else {\n+                    buf->end = buf->start + c->ssl->dyn_rec.size_lo;\n+                }\n+            }\n+        }\n+\n         while (in && buf->last < buf->end && send < limit) {\n             if (in->buf->last_buf || in->buf->flush) {\n                 flush = 1;\n@@ -2507,6 +2543,9 @@\n \n     if (n > 0) {\n \n+        c->ssl->dyn_rec_records_sent++;\n+        c->ssl->dyn_rec_last_write = ngx_current_msec;\n+\n         if (c->ssl->saved_read_handler) {\n \n             c->read->handler = c->ssl->saved_read_handler;\ndiff -uNr a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h\n--- a/src/event/ngx_event_openssl.h\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/event/ngx_event_openssl.h\t2020-01-02 21:25:20.000000000 +0800\n@@ -64,10 +64,19 @@\n #endif\n \n \n+typedef struct {\n+    ngx_msec_t                  timeout;\n+    ngx_uint_t                  threshold;\n+    size_t                      size_lo;\n+    size_t                      size_hi;\n+} ngx_ssl_dyn_rec_t;\n+\n+\n struct ngx_ssl_s {\n     SSL_CTX                    *ctx;\n     ngx_log_t                  *log;\n     size_t                      buffer_size;\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n };\n \n \n@@ -99,6 +108,10 @@\n     unsigned                    in_early:1;\n     unsigned                    early_preread:1;\n     unsigned                    write_blocked:1;\n+\n+    ngx_ssl_dyn_rec_t           dyn_rec;\n+    ngx_msec_t                  dyn_rec_last_write;\n+    ngx_uint_t                  dyn_rec_records_sent;\n };\n \n \n@@ -108,7 +121,7 @@\n #define NGX_SSL_DFLT_BUILTIN_SCACHE  -5\n \n \n-#define NGX_SSL_MAX_SESSION_SIZE  4096\n+#define NGX_SSL_MAX_SESSION_SIZE  16384\n \n typedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;\n \ndiff -uNr a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c\n--- a/src/event/ngx_event_quic.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/event/ngx_event_quic.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -0,0 +1,581 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_event.h>\n+\n+\n+/* Limit outgoing packets to 1200 bytes. This is the minimum value allowed. */\n+#define MAX_DATAGRAM_SIZE 1200\n+\n+/* errors */\n+#define NGX_QUIC_NO_ERROR  0x0\n+#define NGX_QUIC_INTERNAL_ERROR  0x1\n+\n+\n+static void ngx_quic_read_handler(ngx_event_t *ev);\n+static void ngx_quic_write_handler(ngx_event_t *ev);\n+\n+static void ngx_quic_handshake_completed(ngx_connection_t *c);\n+\n+static void ngx_quic_shutdown_handler(ngx_event_t *ev);\n+\n+static void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t status);\n+static void ngx_quic_close_connection(ngx_connection_t *c);\n+\n+static ngx_int_t ngx_quic_send_udp_packet(ngx_connection_t *c, uint8_t *buf,\n+    size_t len);\n+\n+\n+static ngx_command_t  ngx_quic_commands[] = {\n+\n+    ngx_null_command\n+};\n+\n+\n+static ngx_core_module_t  ngx_quic_module_ctx = {\n+    ngx_string(\"quic\"),\n+    NULL,\n+    NULL\n+};\n+\n+\n+ngx_module_t  ngx_quic_module = {\n+    NGX_MODULE_V1,\n+    &ngx_quic_module_ctx,                  /* module context */\n+    ngx_quic_commands,                     /* module directives */\n+    NGX_CORE_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+ngx_int_t\n+ngx_quic_create_conf(ngx_quic_t *quic)\n+{\n+    quic->config = quiche_config_new(QUICHE_PROTOCOL_VERSION);\n+    if (quic->config == NULL) {\n+        ngx_log_error(NGX_LOG_EMERG, quic->log, 0, \"failed to create quic config\");\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_validate_initial(ngx_event_t *ev, u_char *buf, ssize_t buf_len)\n+{\n+    /* Check incoming packet type, if it's not Initial we shouldn't be here. */\n+    if (((buf[0] & 0x30) >> 4) != 0) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n+                       \"packet is not quic client initial\");\n+        return NGX_ERROR;\n+    }\n+\n+    /* Client Initial packets must be at least 1200 bytes. */\n+    if (buf_len < QUICHE_MIN_CLIENT_INITIAL_LEN) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n+                       \"quic initial packet is too short\");\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_create_connection(ngx_quic_t *quic, ngx_connection_t *c)\n+{\n+    int                     rc;\n+    u_char                 *buf;\n+    size_t                  buf_len;\n+    quiche_conn            *conn;\n+    static uint8_t          out[MAX_DATAGRAM_SIZE];\n+\n+    uint8_t                 pkt_type;\n+    uint32_t                pkt_version;\n+\n+    uint8_t                 scid[QUICHE_MAX_CONN_ID_LEN];\n+    size_t                  scid_len = sizeof(scid);\n+\n+    uint8_t                 dcid[QUICHE_MAX_CONN_ID_LEN];\n+    size_t                  dcid_len = sizeof(dcid);\n+\n+    uint8_t                 token[1];\n+    size_t                  token_len = sizeof(token);\n+\n+    ngx_quic_connection_t  *qc;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic init connection\");\n+\n+    /* Extract some fields from the client's Initial packet, which was saved\n+     * into c->buffer by ngx_event_recvmsg(). */\n+    buf = c->buffer->pos;\n+    buf_len = ngx_buf_size(c->buffer);\n+\n+    rc = quiche_header_info(buf, buf_len, QUICHE_MAX_CONN_ID_LEN,\n+                            &pkt_version, &pkt_type,\n+                            scid, &scid_len, dcid, &dcid_len,\n+                            token, &token_len);\n+    if (rc < 0) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"failed to parse quic header: %d\", rc);\n+        return NGX_ERROR;\n+    }\n+\n+    /* Version mismatch, do version negotiation. */\n+    if (!quiche_version_is_supported(pkt_version)) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic version negotiation\");\n+\n+        ssize_t written = quiche_negotiate_version(scid, scid_len,\n+                                                   dcid, dcid_len,\n+                                                   out, sizeof(out));\n+\n+        if (written < 0) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                          \"failed to create quic vneg packet: %d\", written);\n+            return NGX_ERROR;\n+        }\n+\n+        if (ngx_quic_send_udp_packet(c, out, written) == NGX_ERROR) {\n+            return NGX_ERROR;\n+        }\n+\n+        return NGX_DONE;\n+    }\n+\n+    /* Initialize source connection ID with some random bytes. */\n+    RAND_bytes(scid, sizeof(scid));\n+\n+#if (NGX_DEBUG)\n+    {\n+    uint8_t dcid_hex[QUICHE_MAX_CONN_ID_LEN * 2],\n+            scid_hex[QUICHE_MAX_CONN_ID_LEN * 2];\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+        \"new quic connection dcid:%*.s new_scid:%*.s\",\n+        ngx_hex_dump(dcid_hex, dcid, dcid_len) - dcid_hex, dcid_hex,\n+        ngx_hex_dump(scid_hex, scid, scid_len) - scid_hex, scid_hex);\n+    }\n+#endif\n+\n+    conn = quiche_conn_new_with_tls(scid, sizeof(scid), NULL, 0, quic->config,\n+                                    c->ssl->connection, true);\n+    if (conn == NULL) {\n+        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to create quic connection\");\n+        return NGX_ERROR;\n+    }\n+\n+    qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t));\n+    if (qc == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    qc->handler = NULL;\n+\n+    qc->conn = conn;\n+\n+    c->quic = qc;\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_handshake(ngx_connection_t *c)\n+{\n+    u_char   *buf;\n+    size_t    buf_len;\n+    ssize_t   done;\n+\n+    c->log->action = \"processing QUIC connection\";\n+\n+    /* Process the client's Initial packet, which was saved into c->buffer by\n+     * ngx_event_recvmsg(). */\n+    buf = c->buffer->pos;\n+    buf_len = ngx_buf_size(c->buffer);\n+\n+    done = quiche_conn_recv(c->quic->conn, buf, buf_len);\n+\n+    if ((done < 0) && (done != QUICHE_ERR_DONE)) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                      \"failed to process quic packet: %d\", done);\n+        return NGX_ERROR;\n+    }\n+\n+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n+        return NGX_ERROR;\n+    }\n+\n+    c->read->handler = ngx_quic_read_handler;\n+    c->write->handler = ngx_quic_write_handler;\n+\n+    ngx_post_event(c->write, &ngx_posted_events);\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static void\n+ngx_quic_read_handler(ngx_event_t *rev)\n+{\n+    int                n;\n+    static uint8_t     buf[65535];\n+    ngx_connection_t  *c;\n+\n+    c = rev->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic read handler\");\n+\n+    if (rev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic connection timed out\");\n+\n+        ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    for (;;) {\n+        n = c->recv(c, buf, sizeof(buf));\n+        if (n == NGX_AGAIN) {\n+            break;\n+        }\n+\n+        if (n == NGX_ERROR) {\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+\n+        ssize_t done = quiche_conn_recv(c->quic->conn, buf, n);\n+\n+        if (done == QUICHE_ERR_DONE) {\n+            break;\n+        }\n+\n+        if (done < 0) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"failed to process quic packet: %d\", done);\n+\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+    }\n+\n+    if (quiche_conn_is_in_early_data(c->quic->conn) ||\n+            quiche_conn_is_established(c->quic->conn)) {\n+        if (!c->ssl->handshaked) {\n+            ngx_quic_handshake_completed(c);\n+        }\n+\n+        if ((c->quic == NULL) || (c->quic->handler == NULL)) {\n+            return;\n+        }\n+\n+        /* Notify application layer that there might be stream data to read. */\n+        c->quic->handler(c);\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic done reading\");\n+\n+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n+        ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    ngx_post_event(c->write, &ngx_posted_events);\n+}\n+\n+\n+static void\n+ngx_quic_write_handler(ngx_event_t *wev)\n+{\n+    ngx_connection_t   *c;\n+    ngx_msec_t          expiry;\n+    static uint8_t      out[MAX_DATAGRAM_SIZE];\n+\n+    c = wev->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic write handler\");\n+\n+    if (wev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic alarm fired\");\n+\n+        quiche_conn_on_timeout(c->quic->conn);\n+    }\n+\n+    if (quiche_conn_is_closed(c->quic->conn)) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic connection is closed\");\n+\n+        ngx_quic_finalize_connection(c, NGX_QUIC_NO_ERROR);\n+        return;\n+    }\n+\n+    for (;;) {\n+        ssize_t written = quiche_conn_send(c->quic->conn, out, sizeof(out));\n+\n+        if (written == QUICHE_ERR_DONE) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic done writing\");\n+            break;\n+        }\n+\n+        if (written < 0) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"failed to create quic packet: %d\", written);\n+\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+\n+        if (ngx_quic_send_udp_packet(c, out, written) == NGX_ERROR) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"failed to send quic packet\");\n+\n+            ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+            return;\n+        }\n+    }\n+\n+    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n+        ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    expiry = quiche_conn_timeout_as_millis(c->quic->conn);\n+    expiry = ngx_max(expiry, 1);\n+\n+    if (wev->timer_set) {\n+        ngx_del_timer(wev);\n+    }\n+\n+    /* quiche_conn_timeout_as_millis() will return UINT64_MAX when the timer\n+     * should be unset (this would be equvalent to returning Option::None in\n+     * Rust). To avoid overflow we need to explicitly check for this value. */\n+    if (expiry != UINT64_MAX) {\n+        ngx_add_timer(wev, expiry);\n+    }\n+}\n+\n+\n+static void\n+ngx_quic_handshake_completed(ngx_connection_t *c)\n+{\n+#if (NGX_DEBUG)\n+    {\n+    char         buf[129], *s, *d;\n+#if OPENSSL_VERSION_NUMBER >= 0x10000000L\n+    const\n+#endif\n+    SSL_CIPHER  *cipher;\n+\n+    cipher = SSL_get_current_cipher(c->ssl->connection);\n+\n+    if (cipher) {\n+        SSL_CIPHER_description(cipher, &buf[1], 128);\n+\n+        for (s = &buf[1], d = buf; *s; s++) {\n+            if (*s == ' ' && *d == ' ') {\n+                continue;\n+            }\n+\n+            if (*s == LF || *s == CR) {\n+                continue;\n+            }\n+\n+            *++d = *s;\n+        }\n+\n+        if (*d != ' ') {\n+            d++;\n+        }\n+\n+        *d = '\\0';\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"QUIC: %s, cipher: \\\"%s\\\"\",\n+                       SSL_get_version(c->ssl->connection), &buf[1]);\n+\n+        if (SSL_session_reused(c->ssl->connection)) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                           \"quic reused session\");\n+        }\n+\n+    } else {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                       \"quic no shared ciphers\");\n+    }\n+    }\n+#endif\n+\n+    ngx_del_timer(c->read);\n+\n+    c->ssl->handshaked = 1;\n+\n+    /* Notify application layer that the handshake is complete. */\n+    c->ssl->handler(c);\n+}\n+\n+\n+ngx_int_t\n+ngx_quic_shutdown(ngx_connection_t *c)\n+{\n+    if (!quiche_conn_is_closed(c->quic->conn)) {\n+        /* We shouldn't free the connection state yet, as we need to wait for\n+         * the draining timeout to expire. Setup event handlers such that we\n+         * will try again when that happens (or when another event is\n+         * triggered). */\n+        c->read->handler = ngx_quic_shutdown_handler;\n+        c->write->handler = ngx_quic_shutdown_handler;\n+\n+        /* We need to flush any remaining frames to the client (including\n+         * CONNECTION_CLOSE), so invoke the write handler. This also takes\n+         * care of setting up the draining timer. */\n+        ngx_quic_write_handler(c->write);\n+\n+        /* The QUIC connection might have already been freed inside the write\n+         * handler, in which case we are done. */\n+        if (c->destroyed) {\n+            return NGX_OK;\n+        }\n+\n+        return NGX_AGAIN;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"free quic connection\");\n+\n+    quiche_conn_free(c->quic->conn);\n+\n+    c->quic = NULL;\n+    c->ssl = NULL;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_quic_shutdown_handler(ngx_event_t *ev)\n+{\n+    ngx_connection_t           *c;\n+    ngx_connection_handler_pt   handler;\n+\n+    c = ev->data;\n+    handler = c->quic->handler;\n+\n+    if (ev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic alarm fired\");\n+\n+        quiche_conn_on_timeout(c->quic->conn);\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"quic shutdown handler\");\n+\n+    if (ngx_quic_shutdown(c) == NGX_AGAIN) {\n+        return;\n+    }\n+\n+    handler(c);\n+}\n+\n+\n+static void\n+ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t status)\n+{\n+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                   \"finalize quic connection: %d\", c->fd);\n+\n+    c->error = 1;\n+\n+    quiche_conn_close(c->quic->conn, false, status, NULL, 0);\n+\n+    /* Notify the application layer that the connection is in an error\n+     * state and will be closed. */\n+    if (c->quic->handler != NULL) {\n+        c->quic->handler(c);\n+        return;\n+    }\n+\n+    ngx_quic_close_connection(c);\n+}\n+\n+\n+static void\n+ngx_quic_close_connection(ngx_connection_t *c)\n+{\n+    ngx_pool_t  *pool;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n+                   \"close quic connection: %d\", c->fd);\n+\n+    if (c->quic) {\n+        if (ngx_quic_shutdown(c) == NGX_AGAIN) {\n+            c->quic->handler = ngx_quic_close_connection;\n+            return;\n+        }\n+\n+        if (c->destroyed) {\n+            return;\n+        }\n+    }\n+\n+#if (NGX_STAT_STUB)\n+    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);\n+#endif\n+\n+    c->destroyed = 1;\n+\n+    pool = c->pool;\n+\n+    ngx_close_connection(c);\n+\n+    ngx_destroy_pool(pool);\n+}\n+\n+\n+void\n+ngx_quic_cleanup_ctx(void *data)\n+{\n+    ngx_quic_t  *quic = data;\n+\n+    quiche_config_free(quic->config);\n+}\n+\n+\n+static ngx_int_t\n+ngx_quic_send_udp_packet(ngx_connection_t *c, uint8_t *buf, size_t len)\n+{\n+    ngx_buf_t    out_buf = {0};\n+    ngx_chain_t  out_chain = {0};\n+\n+    /* The send_chain() API takes an ngx_chain_t parameter instead of a simple\n+     * buffer, so we need to initialize the chain such that it contains only a\n+     * single buffer.\n+     *\n+     * The c->send_chain() call is required (instead of just c->send()) because\n+     * it uses the sendmsg(2) syscall (instead of sendto(2)), which allows us to\n+     * specify the correct source IP address for the connection. */\n+\n+    out_buf.start = out_buf.pos = buf;\n+    out_buf.end = out_buf.last = buf + len;\n+    out_buf.memory = 1;\n+    out_buf.flush = 1;\n+\n+    out_chain.buf = &out_buf;\n+    out_chain.next = NULL;\n+\n+    if (c->send_chain(c, &out_chain, 0) == NGX_CHAIN_ERROR) {\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\ndiff -uNr a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h\n--- a/src/event/ngx_event_quic.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/event/ngx_event_quic.h\t2020-01-02 21:25:20.000000000 +0800\n@@ -0,0 +1,49 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#ifndef _NGX_EVENT_QUIC_H_INCLUDED_\n+#define _NGX_EVENT_QUIC_H_INCLUDED_\n+\n+\n+#include <stdbool.h>\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+\n+#include <quiche.h>\n+\n+typedef struct ngx_quic_s              ngx_quic_t;\n+typedef struct ngx_quic_connection_s   ngx_quic_connection_t;\n+\n+struct ngx_quic_s {\n+    quiche_config              *config;\n+    ngx_log_t                  *log;\n+};\n+\n+struct ngx_quic_connection_s {\n+    quiche_conn                *conn;\n+\n+    ngx_connection_handler_pt   handler;\n+};\n+\n+\n+ngx_int_t ngx_quic_create_conf(ngx_quic_t *quic);\n+\n+ngx_int_t ngx_quic_validate_initial(ngx_event_t *ev, u_char *buf,\n+    ssize_t buf_len);\n+\n+ngx_int_t ngx_quic_create_connection(ngx_quic_t *quic, ngx_connection_t *c);\n+\n+ngx_int_t ngx_quic_create_ssl_connection(ngx_ssl_t *ssl, ngx_connection_t *c,\n+    ngx_uint_t flags);\n+\n+ngx_int_t ngx_quic_handshake(ngx_connection_t *c);\n+\n+ngx_int_t ngx_quic_shutdown(ngx_connection_t *c);\n+\n+void ngx_quic_cleanup_ctx(void *data);\n+\n+#endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */\ndiff -uNr a/src/event/ngx_event_udp.c b/src/event/ngx_event_udp.c\n--- a/src/event/ngx_event_udp.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/event/ngx_event_udp.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -276,6 +276,14 @@\n         (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);\n #endif\n \n+#if (NGX_QUIC)\n+        if (ls->quic) {\n+            if (ngx_quic_validate_initial(ev, buffer, n) != NGX_OK) {\n+                goto next;\n+            }\n+        }\n+#endif\n+\n         ngx_accept_disabled = ngx_cycle->connection_n / 8\n                               - ngx_cycle->free_connection_n;\n \ndiff -uNr a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c\n--- a/src/http/modules/ngx_http_ssl_module.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.c\t2020-01-03 10:59:08.000000000 +0800\n@@ -249,6 +249,41 @@\n       offsetof(ngx_http_ssl_srv_conf_t, early_data),\n       NULL },\n \n+    { ngx_string(\"ssl_dyn_rec_enable\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_flag_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_lo\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_size_hi\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi),\n+      NULL },\n+\n+    { ngx_string(\"ssl_dyn_rec_threshold\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold),\n+      NULL },\n+\n       ngx_null_command\n };\n \n@@ -371,10 +406,10 @@\n #if (NGX_DEBUG)\n     unsigned int            i;\n #endif\n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY || NGX_HTTP_V3)\n     ngx_http_connection_t  *hc;\n #endif\n-#if (NGX_HTTP_V2 || NGX_DEBUG)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY || NGX_DEBUG)\n     ngx_connection_t       *c;\n \n     c = ngx_ssl_get_connection(ssl_conn);\n@@ -388,9 +423,20 @@\n     }\n #endif\n \n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY || NGX_HTTP_V3)\n     hc = c->data;\n+#endif\n \n+#if (NGX_HTTP_V2 && NGX_HTTP_SPDY)\n+    if (hc->addr_conf->http2 && hc->addr_conf->spdy) {\n+        srv = (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE\n+                                NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n+        srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE NGX_SPDY_NPN_ADVERTISE\n+                        NGX_HTTP_NPN_ADVERTISE) - 1;\n+\n+    } else\n+#endif\n+#if (NGX_HTTP_V2)\n     if (hc->addr_conf->http2) {\n         srv =\n            (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n@@ -398,6 +444,20 @@\n \n     } else\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (hc->addr_conf->spdy) {\n+        srv = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n+        srvlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;\n+\n+    } else\n+#endif\n+#if (NGX_HTTP_V3)\n+    if (hc->addr_conf->quic) {\n+        srv = (unsigned char *) QUICHE_H3_APPLICATION_PROTOCOL;\n+        srvlen = sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1;\n+\n+    } else\n+#endif\n     {\n         srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;\n         srvlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;\n@@ -425,19 +485,32 @@\n ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,\n     const unsigned char **out, unsigned int *outlen, void *arg)\n {\n-#if (NGX_HTTP_V2 || NGX_DEBUG)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY || NGX_DEBUG)\n     ngx_connection_t  *c;\n \n     c = ngx_ssl_get_connection(ssl_conn);\n     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"SSL NPN advertised\");\n #endif\n \n-#if (NGX_HTTP_V2)\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY)\n     {\n     ngx_http_connection_t  *hc;\n \n     hc = c->data;\n+#endif\n+ \n+#if (NGX_HTTP_V2 && NGX_HTTP_SPDY)\n+    if (hc->addr_conf->http2 && hc->addr_conf->spdy) {\n+        *out = (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE\n+                                 NGX_SPDY_NPN_ADVERTISE\n+                                 NGX_HTTP_NPN_ADVERTISE;\n+        *outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE NGX_SPDY_NPN_ADVERTISE\n+                         NGX_HTTP_NPN_ADVERTISE) - 1;\n \n+        return SSL_TLSEXT_ERR_OK;\n+    } else\n+#endif\n+#if (NGX_HTTP_V2)\n     if (hc->addr_conf->http2) {\n         *out =\n             (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n@@ -445,6 +518,20 @@\n \n         return SSL_TLSEXT_ERR_OK;\n     }\n+#endif\n+#if (NGX_HTTP_V2 && NGX_HTTP_SPDY)\n+    else\n+#endif\n+#if (NGX_HTTP_SPDY)\n+    if (hc->addr_conf->spdy) {\n+        *out = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;\n+        *outlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;\n+\n+        return SSL_TLSEXT_ERR_OK;\n+    }\n+#endif\n+\n+#if (NGX_HTTP_V2 || NGX_HTTP_SPDY)\n     }\n #endif\n \n@@ -580,6 +667,11 @@\n     sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;\n     sscf->stapling = NGX_CONF_UNSET;\n     sscf->stapling_verify = NGX_CONF_UNSET;\n+    sscf->dyn_rec_enable = NGX_CONF_UNSET;\n+    sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC;\n+    sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE;\n+    sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT;\n \n     return sscf;\n }\n@@ -647,6 +739,20 @@\n     ngx_conf_merge_str_value(conf->stapling_responder,\n                          prev->stapling_responder, \"\");\n \n+    ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0);\n+    ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout,\n+                             1000);\n+    /* Default sizes for the dynamic record sizes are defined to fit maximal\n+       TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi:\n+       1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo,\n+                             1369);\n+    /* 4229 = (1500 - 40 - 20 - 10) * 3  - 61 */\n+    ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi,\n+                             4229);\n+    ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold,\n+                             40);\n+\n     conf->ssl.log = cf->log;\n \n     if (conf->enable) {\n@@ -857,6 +963,28 @@\n         return NGX_CONF_ERROR;\n     }\n \n+    if (conf->dyn_rec_enable) {\n+        conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout;\n+        conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold;\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_lo) {\n+            conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_lo = conf->buffer_size;\n+        }\n+\n+        if (conf->buffer_size > conf->dyn_rec_size_hi) {\n+            conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi;\n+\n+        } else {\n+            conf->ssl.dyn_rec.size_hi = conf->buffer_size;\n+        }\n+\n+    } else {\n+        conf->ssl.dyn_rec.timeout = 0;\n+    }\n+\n     return NGX_CONF_OK;\n }\n \ndiff -uNr a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h\n--- a/src/http/modules/ngx_http_ssl_module.h\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/modules/ngx_http_ssl_module.h\t2020-01-02 21:25:20.000000000 +0800\n@@ -61,6 +61,12 @@\n \n     u_char                         *file;\n     ngx_uint_t                      line;\n+\n+    ngx_flag_t                      dyn_rec_enable;\n+    ngx_msec_t                      dyn_rec_timeout;\n+    size_t                          dyn_rec_size_lo;\n+    size_t                          dyn_rec_size_hi;\n+    ngx_uint_t                      dyn_rec_threshold;\n } ngx_http_ssl_srv_conf_t;\n \n \ndiff -uNr a/src/http/ngx_http.c b/src/http/ngx_http.c\n--- a/src/http/ngx_http.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/ngx_http.c\t2020-01-03 11:38:04.000000000 +0800\n@@ -1141,6 +1141,7 @@\n ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,\n     ngx_http_listen_opt_t *lsopt)\n {\n+    int                         t;\n     in_port_t                   p;\n     ngx_uint_t                  i;\n     struct sockaddr            *sa;\n@@ -1159,11 +1160,13 @@\n \n     sa = lsopt->sockaddr;\n     p = ngx_inet_get_port(sa);\n+    t = lsopt->quic ? SOCK_DGRAM : SOCK_STREAM;\n \n     port = cmcf->ports->elts;\n     for (i = 0; i < cmcf->ports->nelts; i++) {\n \n-        if (p != port[i].port || sa->sa_family != port[i].family) {\n+        if (p != port[i].port || sa->sa_family != port[i].family\n+             || t != port[i].type) {\n             continue;\n         }\n \n@@ -1182,6 +1185,7 @@\n     port->family = sa->sa_family;\n     port->port = p;\n     port->addrs.elts = NULL;\n+    port->type = t;\n \n     return ngx_http_add_address(cf, cscf, port, lsopt);\n }\n@@ -1199,6 +1203,12 @@\n #if (NGX_HTTP_V2)\n     ngx_uint_t             http2;\n #endif\n+#if (NGX_HTTP_SPDY)\n+    ngx_uint_t             spdy;\n+#endif\n+#if (NGX_HTTP_V3)\n+    ngx_uint_t             quic;\n+#endif\n \n     /*\n      * we cannot compare whole sockaddr struct's as kernel\n@@ -1234,6 +1244,12 @@\n #if (NGX_HTTP_V2)\n         http2 = lsopt->http2 || addr[i].opt.http2;\n #endif\n+#if (NGX_HTTP_SPDY)\n+        spdy = lsopt->spdy || addr[i].opt.spdy;\n+#endif\n+#if (NGX_HTTP_V3)\n+        quic = lsopt->quic || addr[i].opt.quic;\n+#endif\n \n         if (lsopt->set) {\n \n@@ -1270,6 +1286,12 @@\n #if (NGX_HTTP_V2)\n         addr[i].opt.http2 = http2;\n #endif\n+#if (NGX_HTTP_SPDY)\n+        addr[i].opt.spdy = spdy;\n+#endif\n+#if (NGX_HTTP_V3)\n+        addr[i].opt.quic = quic;\n+#endif\n \n         return NGX_OK;\n     }\n@@ -1313,6 +1335,18 @@\n \n #endif\n \n+#if (NGX_HTTP_SPDY && NGX_HTTP_SSL                                            \\\n+     && !defined TLSEXT_TYPE_application_layer_protocol_negotiation           \\\n+     && !defined TLSEXT_TYPE_next_proto_neg)\n+    if (lsopt->spdy && lsopt->ssl) {\n+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n+                           \"nginx was built with OpenSSL that lacks ALPN \"\n+                           \"and NPN support, SPDY is not enabled for %s\",\n+                           lsopt->addr);\n+    }\n+\n+#endif\n+\n     addr = ngx_array_push(&port->addrs);\n     if (addr == NULL) {\n         return NGX_ERROR;\n@@ -1688,6 +1722,12 @@\n             break;\n         }\n \n+#if (NGX_HTTP_V3)\n+        if (addr[i].opt.quic) {\n+            ls->type = SOCK_DGRAM;\n+        }\n+#endif\n+\n         addr++;\n         last--;\n     }\n@@ -1770,6 +1810,12 @@\n     ls->reuseport = addr->opt.reuseport;\n #endif\n \n+#if (NGX_HTTP_V3)\n+    ls->quic = addr->opt.quic;\n+\n+    ls->wildcard = addr->opt.wildcard;\n+#endif\n+\n     return ls;\n }\n \n@@ -1802,7 +1848,13 @@\n #if (NGX_HTTP_V2)\n         addrs[i].conf.http2 = addr[i].opt.http2;\n #endif\n+#if (NGX_HTTP_SPDY)\n+        addrs[i].conf.spdy = addr[i].opt.spdy;\n+#endif\n         addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n+#if (NGX_HTTP_V3)\n+        addrs[i].conf.quic = addr[i].opt.quic;\n+#endif\n \n         if (addr[i].hash.buckets == NULL\n             && (addr[i].wc_head == NULL\n@@ -1868,6 +1920,9 @@\n         addrs6[i].conf.http2 = addr[i].opt.http2;\n #endif\n         addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n+#if (NGX_HTTP_V3)\n+        addrs6[i].conf.quic = addr[i].opt.quic;\n+#endif\n \n         if (addr[i].hash.buckets == NULL\n             && (addr[i].wc_head == NULL\ndiff -uNr a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c\n--- a/src/http/ngx_http_core_module.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/ngx_http_core_module.c\t2020-01-03 11:42:40.000000000 +0800\n@@ -1939,6 +1939,13 @@\n         return NGX_DECLINED;\n     }\n \n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        r->gzip_ok = 1;\n+        return NGX_OK;\n+    }\n+#endif\n+\n     ae = r->headers_in.accept_encoding;\n     if (ae == NULL) {\n         return NGX_DECLINED;\n@@ -2296,6 +2303,9 @@\n #if (NGX_HTTP_V2)\n     sr->stream = r->stream;\n #endif\n+#if (NGX_HTTP_SPDY)\n+    sr->spdy_stream = r->spdy_stream;\n+#endif\n \n     sr->method = NGX_HTTP_GET;\n     sr->http_version = r->http_version;\n@@ -4001,11 +4011,15 @@\n         }\n \n         if (ngx_strcmp(value[n].data, \"spdy\") == 0) {\n-            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n-                               \"invalid parameter \\\"spdy\\\": \"\n-                               \"ngx_http_spdy_module was superseded \"\n-                               \"by ngx_http_v2_module\");\n+#if (NGX_HTTP_SPDY)\n+            lsopt.spdy = 1;\n             continue;\n+#else\n+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                               \"the \\\"spdy\\\" parameter requires \"\n+                               \"ngx_http_spdy_module\");\n+            return NGX_CONF_ERROR;\n+#endif\n         }\n \n         if (ngx_strncmp(value[n].data, \"so_keepalive=\", 13) == 0) {\n@@ -4104,6 +4118,13 @@\n             continue;\n         }\n \n+#if (NGX_HTTP_V3)\n+        if (ngx_strcmp(value[n].data, \"quic\") == 0) {\n+            lsopt.quic = 1;\n+            continue;\n+        }\n+#endif\n+\n         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                            \"invalid parameter \\\"%V\\\"\", &value[n]);\n         return NGX_CONF_ERROR;\ndiff -uNr a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h\n--- a/src/http/ngx_http_core_module.h\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/ngx_http_core_module.h\t2020-01-03 11:43:43.000000000 +0800\n@@ -75,6 +75,9 @@\n     unsigned                   wildcard:1;\n     unsigned                   ssl:1;\n     unsigned                   http2:1;\n+#if (NGX_HTTP_SPDY)\n+    unsigned                   spdy:1;\n+#endif\n #if (NGX_HAVE_INET6)\n     unsigned                   ipv6only:1;\n #endif\n@@ -82,6 +85,7 @@\n     unsigned                   reuseport:1;\n     unsigned                   so_keepalive:2;\n     unsigned                   proxy_protocol:1;\n+    unsigned                   quic:1;\n \n     int                        backlog;\n     int                        rcvbuf;\n@@ -237,7 +241,11 @@\n \n     unsigned                   ssl:1;\n     unsigned                   http2:1;\n+#if (NGX_HTTP_SPDY)\n+    unsigned                   spdy:1;\n+#endif\n     unsigned                   proxy_protocol:1;\n+    unsigned                   quic:1;\n };\n \n \n@@ -268,6 +276,7 @@\n     ngx_int_t                  family;\n     in_port_t                  port;\n     ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */\n+    ngx_int_t                  type;\n } ngx_http_conf_port_t;\n \n \ndiff -uNr a/src/http/ngx_http.h b/src/http/ngx_http.h\n--- a/src/http/ngx_http.h\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/ngx_http.h\t2020-01-03 11:51:41.000000000 +0800\n@@ -18,8 +18,12 @@\n typedef struct ngx_http_cache_s       ngx_http_cache_t;\n typedef struct ngx_http_file_cache_s  ngx_http_file_cache_t;\n typedef struct ngx_http_log_ctx_s     ngx_http_log_ctx_t;\n+#if (NGX_HTTP_SPDY)\n+typedef struct ngx_http_spdy_stream_s ngx_http_spdy_stream_t;\n+#endif\n typedef struct ngx_http_chunked_s     ngx_http_chunked_t;\n typedef struct ngx_http_v2_stream_s   ngx_http_v2_stream_t;\n+typedef struct ngx_http_v3_stream_s   ngx_http_v3_stream_t;\n \n typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,\n     ngx_table_elt_t *h, ngx_uint_t offset);\n@@ -38,6 +42,12 @@\n #if (NGX_HTTP_V2)\n #include <ngx_http_v2.h>\n #endif\n+#if (NGX_HTTP_SPDY)\n+#include <ngx_http_spdy.h>\n+#endif\n+#if (NGX_HTTP_V3)\n+#include <ngx_http_v3.h>\n+#endif\n #if (NGX_HTTP_CACHE)\n #include <ngx_http_cache.h>\n #endif\ndiff -uNr a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c\n--- a/src/http/ngx_http_request_body.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/ngx_http_request_body.c\t2020-01-03 11:53:46.000000000 +0800\n@@ -84,6 +84,18 @@\n         goto done;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        rc = ngx_http_spdy_read_request_body(r, post_handler);\n+        goto done;\n+    }\n+#endif\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        rc = ngx_http_v3_read_request_body(r);\n+        goto done;\n+    }\n+#endif\n \n     preread = r->header_in->last - r->header_in->pos;\n \n@@ -226,6 +238,18 @@\n     }\n #endif\n \n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        rc = ngx_http_v3_read_unbuffered_request_body(r);\n+\n+        if (rc == NGX_OK) {\n+            r->reading_body = 0;\n+        }\n+\n+        return rc;\n+    }\n+#endif\n+\n     if (r->connection->read->timedout) {\n         r->connection->timedout = 1;\n         return NGX_HTTP_REQUEST_TIME_OUT;\n@@ -524,6 +548,18 @@\n         return NGX_OK;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        r->spdy_stream->skip_data = 1;\n+        return NGX_OK;\n+    }\n+#endif\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        r->qstream->skip_data = 1;\n+        return NGX_OK;\n+    }\n+#endif\n \n     if (ngx_http_test_expect(r) != NGX_OK) {\n         return NGX_HTTP_INTERNAL_SERVER_ERROR;\n@@ -809,6 +845,9 @@\n #if (NGX_HTTP_V2)\n         || r->stream != NULL\n #endif\n+#if (NGX_HTTP_V3)\n+        || r->qstream != NULL\n+#endif\n        )\n     {\n         return NGX_OK;\ndiff -uNr a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c\n--- a/src/http/ngx_http_request.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/ngx_http_request.c\t2020-01-03 11:50:40.000000000 +0800\n@@ -64,6 +64,10 @@\n static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);\n #endif\n \n+#if (NGX_HTTP_V3)\n+static void ngx_http_quic_handshake(ngx_event_t *rev);\n+#endif\n+\n \n static char *ngx_http_client_errors[] = {\n \n@@ -324,6 +328,11 @@\n     rev->handler = ngx_http_wait_request_handler;\n     c->write->handler = ngx_http_empty_handler;\n \n+#if (NGX_HTTP_SPDY)\n+    if (hc->addr_conf->spdy) {\n+        rev->handler = ngx_http_spdy_init;\n+    }\n+#endif\n #if (NGX_HTTP_V2)\n     if (hc->addr_conf->http2) {\n         rev->handler = ngx_http_v2_init;\n@@ -349,6 +358,18 @@\n         c->log->action = \"reading PROXY protocol\";\n     }\n \n+#if (NGX_HTTP_V3)\n+    if (hc->addr_conf->quic) {\n+        hc->quic = 1;\n+\n+        /* We already have a UDP packet in the connection buffer, so we don't\n+         * need to wait for another read event to kick-off the handshake. */\n+        ngx_add_timer(rev, c->listening->post_accept_timeout);\n+        ngx_http_quic_handshake(rev);\n+        return;\n+    }\n+#endif\n+\n     if (rev->ready) {\n         /* the deferred accept(), iocp */\n \n@@ -797,7 +818,7 @@\n \n         c->ssl->no_wait_shutdown = 1;\n \n-#if (NGX_HTTP_V2                                                              \\\n+#if ((NGX_HTTP_V2 || NGX_HTTP_V3)                                             \\\n      && (defined TLSEXT_TYPE_application_layer_protocol_negotiation           \\\n          || defined TLSEXT_TYPE_next_proto_neg))\n         {\n@@ -807,7 +828,7 @@\n \n         hc = c->data;\n \n-        if (hc->addr_conf->http2) {\n+        if (hc->addr_conf->http2 || hc->addr_conf->quic) {\n \n #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n             SSL_get0_alpn_selected(c->ssl->connection, &data, &len);\n@@ -822,11 +843,57 @@\n             SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);\n #endif\n \n+        }\n+\n+#if (NGX_HTTP_V2)\n+        if (hc->addr_conf->http2) {\n             if (len == 2 && data[0] == 'h' && data[1] == '2') {\n                 ngx_http_v2_init(c->read);\n                 return;\n             }\n         }\n+#endif\n+\n+#if (NGX_HTTP_SPDY                                                            \\\n+     && (defined TLSEXT_TYPE_application_layer_protocol_negotiation           \\\n+         || defined TLSEXT_TYPE_next_proto_neg))\n+        {\n+        unsigned int             len;\n+        const unsigned char     *data;\n+        static const ngx_str_t   spdy = ngx_string(NGX_SPDY_NPN_NEGOTIATED);\n+\n+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n+        SSL_get0_alpn_selected(c->ssl->connection, &data, &len);\n+\n+#ifdef TLSEXT_TYPE_next_proto_neg\n+        if (len == 0) {\n+            SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);\n+        }\n+#endif\n+\n+#else /* TLSEXT_TYPE_next_proto_neg */\n+        SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);\n+#endif\n+\n+        if (len == spdy.len && ngx_strncmp(data, spdy.data, spdy.len) == 0) {\n+            ngx_http_spdy_init(c->read);\n+            return;\n+        }\n+        }\n+#endif\n+\n+#if (NGX_HTTP_V3)\n+        if (hc->addr_conf->quic) {\n+            if (len >= 2 && data[0] == 'h' && data[1] == '3') {\n+                ngx_http_v3_init(c->read);\n+                return;\n+            }\n+\n+            ngx_http_close_connection(c);\n+            return;\n+        }\n+#endif\n+\n         }\n #endif\n \n@@ -1033,6 +1100,68 @@\n \n #endif\n \n+#if (NGX_HTTP_V3)\n+\n+static void\n+ngx_http_quic_handshake(ngx_event_t *rev)\n+{\n+    ngx_int_t                  rc;\n+    ngx_connection_t          *c;\n+    ngx_http_connection_t     *hc;\n+    ngx_http_v3_srv_conf_t    *qscf;\n+    ngx_http_ssl_srv_conf_t   *sscf;\n+\n+    c = rev->data;\n+    hc = c->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,\n+                   \"http check quic handshake\");\n+\n+    if (rev->timedout) {\n+        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    if (c->close) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, \"https quic handshake\");\n+\n+    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,\n+                                        ngx_http_ssl_module);\n+\n+    if (ngx_ssl_create_connection(&sscf->ssl, c, 0) != NGX_OK) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    qscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);\n+\n+    if (ngx_quic_create_connection(&qscf->quic, c) != NGX_OK) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    rc = ngx_quic_handshake(c);\n+\n+    if (rc == NGX_AGAIN) {\n+\n+        if (!rev->timer_set) {\n+            ngx_add_timer(rev, c->listening->post_accept_timeout);\n+        }\n+\n+        c->ssl->handler = ngx_http_ssl_handshake_handler;\n+        return;\n+    }\n+\n+    ngx_http_ssl_handshake_handler(c);\n+}\n+\n+#endif\n+\n \n static void\n ngx_http_process_request_line(ngx_event_t *rev)\n@@ -2687,6 +2816,20 @@\n     }\n #endif\n \n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        ngx_http_close_request(r, 0);\n+        return;\n+    }\n+#endif\n+\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        ngx_http_close_request(r, 0);\n+        return;\n+    }\n+#endif\n+\n     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n \n     if (r->main->count != 1) {\n@@ -2895,6 +3038,31 @@\n     }\n \n #endif\n+#if (NGX_HTTP_SPDY)\n+\n+    if (r->spdy_stream) {\n+        if (c->error) {\n+            err = 0;\n+            goto closed;\n+        }\n+\n+        return;\n+    }\n+\n+#endif\n+\n+#if (NGX_HTTP_V3)\n+\n+    if (r->qstream) {\n+        if (c->error) {\n+            err = 0;\n+            goto closed;\n+        }\n+\n+        return;\n+    }\n+\n+#endif\n \n #if (NGX_HAVE_KQUEUE)\n \n@@ -3563,7 +3731,22 @@\n     }\n #endif\n \n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        ngx_http_spdy_close_stream(r->spdy_stream, rc);\n+        return;\n+    }\n+#endif\n+\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        ngx_http_v3_close_stream(r->qstream, rc);\n+        return;\n+    }\n+#endif\n+\n     ngx_http_free_request(r, rc);\n+\n     ngx_http_close_connection(c);\n }\n \n@@ -3684,6 +3867,17 @@\n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                    \"close http connection: %d\", c->fd);\n \n+#if (NGX_HTTP_V3)\n+\n+    if (c->quic) {\n+        if (ngx_quic_shutdown(c) == NGX_AGAIN) {\n+            c->quic->handler = ngx_http_close_connection;\n+            return;\n+        }\n+    }\n+\n+#endif\n+\n #if (NGX_HTTP_SSL)\n \n     if (c->ssl) {\ndiff -uNr a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h\n--- a/src/http/ngx_http_request.h\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/ngx_http_request.h\t2020-01-03 11:52:35.000000000 +0800\n@@ -24,6 +24,7 @@\n #define NGX_HTTP_VERSION_10                1000\n #define NGX_HTTP_VERSION_11                1001\n #define NGX_HTTP_VERSION_20                2000\n+#define NGX_HTTP_VERSION_3                 3000\n \n #define NGX_HTTP_UNKNOWN                   0x0001\n #define NGX_HTTP_GET                       0x0002\n@@ -323,6 +324,7 @@\n     ngx_chain_t                      *free;\n \n     unsigned                          ssl:1;\n+    unsigned                          quic:1;\n     unsigned                          proxy_protocol:1;\n } ngx_http_connection_t;\n \n@@ -432,6 +434,9 @@\n     int                              *captures;\n     u_char                           *captures_data;\n #endif\n+#if (NGX_HTTP_SPDY)\n+    ngx_http_spdy_stream_t           *spdy_stream;\n+#endif\n \n     size_t                            limit_rate;\n     size_t                            limit_rate_after;\n@@ -445,6 +450,7 @@\n \n     ngx_http_connection_t            *http_connection;\n     ngx_http_v2_stream_t             *stream;\n+    ngx_http_v3_stream_t             *qstream;\n \n     ngx_http_log_handler_pt           log_handler;\n \ndiff -uNr a/src/http/ngx_http_spdy.c b/src/http/ngx_http_spdy.c\n--- a/src/http/ngx_http_spdy.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy.c\t2020-01-03 11:55:53.000000000 +0800\n@@ -0,0 +1,3701 @@\n+\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_spdy_module.h>\n+\n+#include <zlib.h>\n+\n+\n+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)\n+\n+#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \\\n+    *(uint32_t *) m == (c3 << 24 | c2 << 16 | c1 << 8 | c0)                   \\\n+        && m[4] == c4\n+\n+#else\n+\n+#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \\\n+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4\n+\n+#endif\n+\n+\n+#if (NGX_HAVE_NONALIGNED)\n+\n+#define ngx_spdy_frame_parse_uint16(p)  ntohs(*(uint16_t *) (p))\n+#define ngx_spdy_frame_parse_uint32(p)  ntohl(*(uint32_t *) (p))\n+\n+#else\n+\n+#define ngx_spdy_frame_parse_uint16(p) ((p)[0] << 8 | (p)[1])\n+#define ngx_spdy_frame_parse_uint32(p)                                        \\\n+    ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])\n+\n+#endif\n+\n+#define ngx_spdy_frame_parse_sid(p)                                           \\\n+    (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff)\n+#define ngx_spdy_frame_parse_delta(p)                                         \\\n+    (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff)\n+\n+\n+#define ngx_spdy_ctl_frame_check(h)                                           \\\n+    (((h) & 0xffff0000) == ngx_spdy_ctl_frame_head(0))\n+#define ngx_spdy_data_frame_check(h)                                          \\\n+    (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31))\n+\n+#define ngx_spdy_ctl_frame_type(h)   ((h) & 0x0000ffff)\n+#define ngx_spdy_frame_flags(p)      ((p) >> 24)\n+#define ngx_spdy_frame_length(p)     ((p) & 0x00ffffff)\n+#define ngx_spdy_frame_id(p)         ((p) & 0x00ffffff)\n+\n+\n+#define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE  4096\n+#define NGX_SPDY_CTL_FRAME_BUFFER_SIZE     16\n+\n+#define NGX_SPDY_PROTOCOL_ERROR            1\n+#define NGX_SPDY_INVALID_STREAM            2\n+#define NGX_SPDY_REFUSED_STREAM            3\n+#define NGX_SPDY_UNSUPPORTED_VERSION       4\n+#define NGX_SPDY_CANCEL                    5\n+#define NGX_SPDY_INTERNAL_ERROR            6\n+#define NGX_SPDY_FLOW_CONTROL_ERROR        7\n+#define NGX_SPDY_STREAM_IN_USE             8\n+#define NGX_SPDY_STREAM_ALREADY_CLOSED     9\n+/* deprecated                              10 */\n+#define NGX_SPDY_FRAME_TOO_LARGE           11\n+\n+#define NGX_SPDY_SETTINGS_MAX_STREAMS      4\n+#define NGX_SPDY_SETTINGS_INIT_WINDOW      7\n+\n+#define NGX_SPDY_SETTINGS_FLAG_PERSIST     0x01\n+#define NGX_SPDY_SETTINGS_FLAG_PERSISTED   0x02\n+\n+#define NGX_SPDY_MAX_WINDOW                NGX_MAX_INT32_VALUE\n+#define NGX_SPDY_CONNECTION_WINDOW         65536\n+#define NGX_SPDY_INIT_STREAM_WINDOW        65536\n+#define NGX_SPDY_STREAM_WINDOW             NGX_SPDY_MAX_WINDOW\n+\n+typedef struct {\n+    ngx_uint_t    hash;\n+    u_char        len;\n+    u_char        header[7];\n+    ngx_int_t   (*handler)(ngx_http_request_t *r);\n+} ngx_http_spdy_request_header_t;\n+\n+\n+static void ngx_http_spdy_read_handler(ngx_event_t *rev);\n+static void ngx_http_spdy_write_handler(ngx_event_t *wev);\n+static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc);\n+\n+static u_char *ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+static u_char *ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler);\n+\n+static u_char *ngx_http_spdy_state_inflate_error(\n+    ngx_http_spdy_connection_t *sc, int rc);\n+static u_char *ngx_http_spdy_state_protocol_error(\n+    ngx_http_spdy_connection_t *sc);\n+static u_char *ngx_http_spdy_state_internal_error(\n+    ngx_http_spdy_connection_t *sc);\n+\n+static ngx_int_t ngx_http_spdy_send_window_update(\n+    ngx_http_spdy_connection_t *sc, ngx_uint_t sid, ngx_uint_t delta);\n+static ngx_int_t ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc,\n+    ngx_uint_t sid, ngx_uint_t status, ngx_uint_t priority);\n+static ngx_int_t ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc);\n+static ngx_int_t ngx_http_spdy_settings_frame_handler(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);\n+static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctl_frame(\n+    ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority);\n+static ngx_int_t ngx_http_spdy_ctl_frame_handler(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);\n+\n+static ngx_http_spdy_stream_t *ngx_http_spdy_create_stream(\n+    ngx_http_spdy_connection_t *sc, ngx_uint_t id, ngx_uint_t priority);\n+static ngx_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id(\n+    ngx_http_spdy_connection_t *sc, ngx_uint_t sid);\n+#define ngx_http_spdy_streams_index_size(sscf)  (sscf->streams_index_mask + 1)\n+#define ngx_http_spdy_stream_index(sscf, sid)                                 \\\n+    ((sid >> 1) & sscf->streams_index_mask)\n+\n+static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r);\n+\n+static ngx_int_t ngx_http_spdy_handle_request_header(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_method(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_scheme(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_host(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_path(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_parse_version(ngx_http_request_t *r);\n+\n+static ngx_int_t ngx_http_spdy_construct_request_line(ngx_http_request_t *r);\n+static void ngx_http_spdy_run_request(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r);\n+\n+static ngx_int_t ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream, ngx_uint_t status);\n+\n+static void ngx_http_spdy_close_stream_handler(ngx_event_t *ev);\n+\n+static void ngx_http_spdy_handle_connection_handler(ngx_event_t *rev);\n+static void ngx_http_spdy_keepalive_handler(ngx_event_t *rev);\n+static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,\n+    ngx_int_t rc);\n+\n+static ngx_int_t ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc,\n+    ssize_t delta);\n+\n+static void ngx_http_spdy_pool_cleanup(void *data);\n+\n+static void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size);\n+static void ngx_http_spdy_zfree(void *opaque, void *address);\n+\n+\n+static const u_char ngx_http_spdy_dict[] = {\n+    0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69,   /* - - - - o p t i */\n+    0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68,   /* o n s - - - - h */\n+    0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70,   /* e a d - - - - p */\n+    0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70,   /* o s t - - - - p */\n+    0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65,   /* u t - - - - d e */\n+    0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05,   /* l e t e - - - - */\n+    0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00,   /* t r a c e - - - */\n+    0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00,   /* - a c c e p t - */\n+    0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70,   /* - - - a c c e p */\n+    0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   /* t - c h a r s e */\n+    0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63,   /* t - - - - a c c */\n+    0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   /* e p t - e n c o */\n+    0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f,   /* d i n g - - - - */\n+    0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c,   /* a c c e p t - l */\n+    0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00,   /* a n g u a g e - */\n+    0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70,   /* - - - a c c e p */\n+    0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73,   /* t - r a n g e s */\n+    0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00,   /* - - - - a g e - */\n+    0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77,   /* - - - a l l o w */\n+    0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68,   /* - - - - a u t h */\n+    0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,   /* o r i z a t i o */\n+    0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63,   /* n - - - - c a c */\n+    0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72,   /* h e - c o n t r */\n+    0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f,   /* o l - - - - c o */\n+    0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,   /* n n e c t i o n */\n+    0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74,   /* - - - - c o n t */\n+    0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65,   /* e n t - b a s e */\n+    0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74,   /* - - - - c o n t */\n+    0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   /* e n t - e n c o */\n+    0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10,   /* d i n g - - - - */\n+    0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,   /* c o n t e n t - */\n+    0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,   /* l a n g u a g e */\n+    0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74,   /* - - - - c o n t */\n+    0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67,   /* e n t - l e n g */\n+    0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f,   /* t h - - - - c o */\n+    0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f,   /* n t e n t - l o */\n+    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,   /* c a t i o n - - */\n+    0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   /* - - c o n t e n */\n+    0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00,   /* t - m d 5 - - - */\n+    0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,   /* - c o n t e n t */\n+    0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00,   /* - r a n g e - - */\n+    0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   /* - - c o n t e n */\n+    0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00,   /* t - t y p e - - */\n+    0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00,   /* - - d a t e - - */\n+    0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00,   /* - - e t a g - - */\n+    0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,   /* - - e x p e c t */\n+    0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69,   /* - - - - e x p i */\n+    0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66,   /* r e s - - - - f */\n+    0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68,   /* r o m - - - - h */\n+    0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69,   /* o s t - - - - i */\n+    0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00,   /* f - m a t c h - */\n+    0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f,   /* - - - i f - m o */\n+    0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73,   /* d i f i e d - s */\n+    0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d,   /* i n c e - - - - */\n+    0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d,   /* i f - n o n e - */\n+    0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00,   /* m a t c h - - - */\n+    0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67,   /* - i f - r a n g */\n+    0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d,   /* e - - - - i f - */\n+    0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,   /* u n m o d i f i */\n+    0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65,   /* e d - s i n c e */\n+    0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74,   /* - - - - l a s t */\n+    0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,   /* - m o d i f i e */\n+    0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63,   /* d - - - - l o c */\n+    0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,   /* a t i o n - - - */\n+    0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72,   /* - m a x - f o r */\n+    0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00,   /* w a r d s - - - */\n+    0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00,   /* - p r a g m a - */\n+    0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79,   /* - - - p r o x y */\n+    0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,   /* - a u t h e n t */\n+    0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00,   /* i c a t e - - - */\n+    0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61,   /* - p r o x y - a */\n+    0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,   /* u t h o r i z a */\n+    0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05,   /* t i o n - - - - */\n+    0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00,   /* r a n g e - - - */\n+    0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72,   /* - r e f e r e r */\n+    0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72,   /* - - - - r e t r */\n+    0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00,   /* y - a f t e r - */\n+    0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,   /* - - - s e r v e */\n+    0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00,   /* r - - - - t e - */\n+    0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c,   /* - - - t r a i l */\n+    0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72,   /* e r - - - - t r */\n+    0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65,   /* a n s f e r - e */\n+    0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00,   /* n c o d i n g - */\n+    0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61,   /* - - - u p g r a */\n+    0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73,   /* d e - - - - u s */\n+    0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74,   /* e r - a g e n t */\n+    0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79,   /* - - - - v a r y */\n+    0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00,   /* - - - - v i a - */\n+    0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69,   /* - - - w a r n i */\n+    0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77,   /* n g - - - - w w */\n+    0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,   /* w - a u t h e n */\n+    0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00,   /* t i c a t e - - */\n+    0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,   /* - - m e t h o d */\n+    0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00,   /* - - - - g e t - */\n+    0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,   /* - - - s t a t u */\n+    0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30,   /* s - - - - 2 0 0 */\n+    0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76,   /* - O K - - - - v */\n+    0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00,   /* e r s i o n - - */\n+    0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,   /* - - H T T P - 1 */\n+    0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,   /* - 1 - - - - u r */\n+    0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62,   /* l - - - - p u b */\n+    0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73,   /* l i c - - - - s */\n+    0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69,   /* e t - c o o k i */\n+    0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65,   /* e - - - - k e e */\n+    0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00,   /* p - a l i v e - */\n+    0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69,   /* - - - o r i g i */\n+    0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32,   /* n 1 0 0 1 0 1 2 */\n+    0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35,   /* 0 1 2 0 2 2 0 5 */\n+    0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30,   /* 2 0 6 3 0 0 3 0 */\n+    0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33,   /* 2 3 0 3 3 0 4 3 */\n+    0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37,   /* 0 5 3 0 6 3 0 7 */\n+    0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30,   /* 4 0 2 4 0 5 4 0 */\n+    0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34,   /* 6 4 0 7 4 0 8 4 */\n+    0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31,   /* 0 9 4 1 0 4 1 1 */\n+    0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31,   /* 4 1 2 4 1 3 4 1 */\n+    0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34,   /* 4 4 1 5 4 1 6 4 */\n+    0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34,   /* 1 7 5 0 2 5 0 4 */\n+    0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e,   /* 5 0 5 2 0 3 - N */\n+    0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f,   /* o n - A u t h o */\n+    0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65,   /* r i t a t i v e */\n+    0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,   /* - I n f o r m a */\n+    0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20,   /* t i o n 2 0 4 - */\n+    0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65,   /* N o - C o n t e */\n+    0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f,   /* n t 3 0 1 - M o */\n+    0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d,   /* v e d - P e r m */\n+    0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34,   /* a n e n t l y 4 */\n+    0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52,   /* 0 0 - B a d - R */\n+    0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30,   /* e q u e s t 4 0 */\n+    0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68,   /* 1 - U n a u t h */\n+    0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30,   /* o r i z e d 4 0 */\n+    0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64,   /* 3 - F o r b i d */\n+    0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e,   /* d e n 4 0 4 - N */\n+    0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64,   /* o t - F o u n d */\n+    0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65,   /* 5 0 0 - I n t e */\n+    0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72,   /* r n a l - S e r */\n+    0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f,   /* v e r - E r r o */\n+    0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74,   /* r 5 0 1 - N o t */\n+    0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,   /* - I m p l e m e */\n+    0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20,   /* n t e d 5 0 3 - */\n+    0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20,   /* S e r v i c e - */\n+    0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,   /* U n a v a i l a */\n+    0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46,   /* b l e J a n - F */\n+    0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41,   /* e b - M a r - A */\n+    0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a,   /* p r - M a y - J */\n+    0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41,   /* u n - J u l - A */\n+    0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20,   /* u g - S e p t - */\n+    0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20,   /* O c t - N o v - */\n+    0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30,   /* D e c - 0 0 - 0 */\n+    0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e,   /* 0 - 0 0 - M o n */\n+    0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57,   /* - - T u e - - W */\n+    0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c,   /* e d - - T h u - */\n+    0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61,   /* - F r i - - S a */\n+    0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20,   /* t - - S u n - - */\n+    0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b,   /* G M T c h u n k */\n+    0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f,   /* e d - t e x t - */\n+    0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61,   /* h t m l - i m a */\n+    0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69,   /* g e - p n g - i */\n+    0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67,   /* m a g e - j p g */\n+    0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67,   /* - i m a g e - g */\n+    0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   /* i f - a p p l i */\n+    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   /* c a t i o n - x */\n+    0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   /* m l - a p p l i */\n+    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   /* c a t i o n - x */\n+    0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c,   /* h t m l - x m l */\n+    0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c,   /* - t e x t - p l */\n+    0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74,   /* a i n - t e x t */\n+    0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72,   /* - j a v a s c r */\n+    0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c,   /* i p t - p u b l */\n+    0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,   /* i c p r i v a t */\n+    0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65,   /* e m a x - a g e */\n+    0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65,   /* - g z i p - d e */\n+    0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64,   /* f l a t e - s d */\n+    0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   /* c h c h a r s e */\n+    0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63,   /* t - u t f - 8 c */\n+    0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69,   /* h a r s e t - i */\n+    0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d,   /* s o - 8 8 5 9 - */\n+    0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a,   /* 1 - u t f - - - */\n+    0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e          /* - e n q - 0 -   */\n+};\n+\n+\n+static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = {\n+    { 0, 6, \"method\", ngx_http_spdy_parse_method },\n+    { 0, 6, \"scheme\", ngx_http_spdy_parse_scheme },\n+    { 0, 4, \"host\", ngx_http_spdy_parse_host },\n+    { 0, 4, \"path\", ngx_http_spdy_parse_path },\n+    { 0, 7, \"version\", ngx_http_spdy_parse_version },\n+};\n+\n+#define NGX_SPDY_REQUEST_HEADERS                                              \\\n+    (sizeof(ngx_http_spdy_request_headers)                                    \\\n+     / sizeof(ngx_http_spdy_request_header_t))\n+\n+\n+void\n+ngx_http_spdy_init(ngx_event_t *rev)\n+{\n+    int                          rc;\n+    ngx_connection_t            *c;\n+    ngx_pool_cleanup_t          *cln;\n+    ngx_http_connection_t       *hc;\n+    ngx_http_spdy_srv_conf_t    *sscf;\n+    ngx_http_spdy_main_conf_t   *smcf;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    c = rev->data;\n+    hc = c->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"init spdy request\");\n+\n+    c->log->action = \"processing SPDY\";\n+\n+    smcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_spdy_module);\n+\n+    if (smcf->recv_buffer == NULL) {\n+        smcf->recv_buffer = ngx_palloc(ngx_cycle->pool, smcf->recv_buffer_size);\n+        if (smcf->recv_buffer == NULL) {\n+            ngx_http_close_connection(c);\n+            return;\n+        }\n+    }\n+\n+    sc = ngx_pcalloc(c->pool, sizeof(ngx_http_spdy_connection_t));\n+    if (sc == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->connection = c;\n+    sc->http_connection = hc;\n+\n+    sc->send_window = NGX_SPDY_CONNECTION_WINDOW;\n+    sc->recv_window = NGX_SPDY_CONNECTION_WINDOW;\n+\n+    sc->init_window = NGX_SPDY_INIT_STREAM_WINDOW;\n+\n+    sc->handler = hc->proxy_protocol ? ngx_http_spdy_proxy_protocol\n+                                     : ngx_http_spdy_state_head;\n+\n+    sc->zstream_in.zalloc = ngx_http_spdy_zalloc;\n+    sc->zstream_in.zfree = ngx_http_spdy_zfree;\n+    sc->zstream_in.opaque = sc;\n+\n+    rc = inflateInit(&sc->zstream_in);\n+    if (rc != Z_OK) {\n+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n+                      \"inflateInit() failed: %d\", rc);\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->zstream_out.zalloc = ngx_http_spdy_zalloc;\n+    sc->zstream_out.zfree = ngx_http_spdy_zfree;\n+    sc->zstream_out.opaque = sc;\n+\n+    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_spdy_module);\n+\n+    rc = deflateInit2(&sc->zstream_out, (int) sscf->headers_comp,\n+                      Z_DEFLATED, 11, 4, Z_DEFAULT_STRATEGY);\n+\n+    if (rc != Z_OK) {\n+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n+                      \"deflateInit2() failed: %d\", rc);\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    rc = deflateSetDictionary(&sc->zstream_out, ngx_http_spdy_dict,\n+                              sizeof(ngx_http_spdy_dict));\n+    if (rc != Z_OK) {\n+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n+                      \"deflateSetDictionary() failed: %d\", rc);\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);\n+    if (sc->pool == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t));\n+    if (cln == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    cln->handler = ngx_http_spdy_pool_cleanup;\n+    cln->data = sc;\n+\n+    sc->streams_index = ngx_pcalloc(sc->pool,\n+                                    ngx_http_spdy_streams_index_size(sscf)\n+                                    * sizeof(ngx_http_spdy_stream_t *));\n+    if (sc->streams_index == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    if (ngx_http_spdy_send_settings(sc) == NGX_ERROR) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    if (ngx_http_spdy_send_window_update(sc, 0, NGX_SPDY_MAX_WINDOW\n+                                                - sc->recv_window)\n+        == NGX_ERROR)\n+    {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->recv_window = NGX_SPDY_MAX_WINDOW;\n+\n+    ngx_queue_init(&sc->waiting);\n+    ngx_queue_init(&sc->posted);\n+\n+    c->data = sc;\n+\n+    rev->handler = ngx_http_spdy_read_handler;\n+    c->write->handler = ngx_http_spdy_write_handler;\n+\n+    ngx_http_spdy_read_handler(rev);\n+}\n+\n+\n+static void\n+ngx_http_spdy_read_handler(ngx_event_t *rev)\n+{\n+    u_char                      *p, *end;\n+    size_t                       available;\n+    ssize_t                      n;\n+    ngx_connection_t            *c;\n+    ngx_http_spdy_main_conf_t   *smcf;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    c = rev->data;\n+    sc = c->data;\n+\n+    if (rev->timedout) {\n+        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_REQUEST_TIME_OUT);\n+        return;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"spdy read handler\");\n+\n+    sc->blocked = 1;\n+\n+    smcf = ngx_http_get_module_main_conf(sc->http_connection->conf_ctx,\n+                                         ngx_http_spdy_module);\n+\n+    available = smcf->recv_buffer_size - 2 * NGX_SPDY_STATE_BUFFER_SIZE;\n+\n+    do {\n+        p = smcf->recv_buffer;\n+\n+        ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE);\n+        end = p + sc->buffer_used;\n+\n+        n = c->recv(c, end, available);\n+\n+        if (n == NGX_AGAIN) {\n+            break;\n+        }\n+\n+        if (n == 0 && (sc->incomplete || sc->processing)) {\n+            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n+                          \"client prematurely closed connection\");\n+        }\n+\n+        if (n == 0 || n == NGX_ERROR) {\n+            ngx_http_spdy_finalize_connection(sc,\n+                                              NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+            return;\n+        }\n+\n+        end += n;\n+\n+        sc->buffer_used = 0;\n+        sc->incomplete = 0;\n+\n+        do {\n+            p = sc->handler(sc, p, end);\n+\n+            if (p == NULL) {\n+                return;\n+            }\n+\n+        } while (p != end);\n+\n+    } while (rev->ready);\n+\n+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return;\n+    }\n+\n+    if (sc->last_out && ngx_http_spdy_send_output_queue(sc) == NGX_ERROR) {\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+        return;\n+    }\n+\n+    sc->blocked = 0;\n+\n+    if (sc->processing) {\n+        if (rev->timer_set) {\n+            ngx_del_timer(rev);\n+        }\n+        return;\n+    }\n+\n+    ngx_http_spdy_handle_connection(sc);\n+}\n+\n+\n+static void\n+ngx_http_spdy_write_handler(ngx_event_t *wev)\n+{\n+    ngx_int_t                    rc;\n+    ngx_queue_t                 *q;\n+    ngx_connection_t            *c;\n+    ngx_http_spdy_stream_t      *stream;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    c = wev->data;\n+    sc = c->data;\n+\n+    if (wev->timedout) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"spdy write event timed out\");\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+        return;\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"spdy write handler\");\n+\n+    sc->blocked = 1;\n+\n+    rc = ngx_http_spdy_send_output_queue(sc);\n+\n+    if (rc == NGX_ERROR) {\n+        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+        return;\n+    }\n+\n+    while (!ngx_queue_empty(&sc->posted)) {\n+        q = ngx_queue_head(&sc->posted);\n+\n+        ngx_queue_remove(q);\n+\n+        stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);\n+\n+        stream->handled = 0;\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"run spdy stream %ui\", stream->id);\n+\n+        wev = stream->request->connection->write;\n+        wev->handler(wev);\n+    }\n+\n+    sc->blocked = 0;\n+\n+    if (rc == NGX_AGAIN) {\n+        return;\n+    }\n+\n+    ngx_http_spdy_handle_connection(sc);\n+}\n+\n+\n+ngx_int_t\n+ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc)\n+{\n+    int                         tcp_nodelay;\n+    ngx_chain_t                *cl;\n+    ngx_event_t                *wev;\n+    ngx_connection_t           *c;\n+    ngx_http_core_loc_conf_t   *clcf;\n+    ngx_http_spdy_out_frame_t  *out, *frame, *fn;\n+\n+    c = sc->connection;\n+\n+    if (c->error) {\n+        return NGX_ERROR;\n+    }\n+\n+    wev = c->write;\n+\n+    if (!wev->ready) {\n+        return NGX_OK;\n+    }\n+\n+    cl = NULL;\n+    out = NULL;\n+\n+    for (frame = sc->last_out; frame; frame = fn) {\n+        frame->last->next = cl;\n+        cl = frame->first;\n+\n+        fn = frame->next;\n+        frame->next = out;\n+        out = frame;\n+\n+        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"spdy frame out: %p sid:%ui prio:%ui bl:%d len:%uz\",\n+                       out, out->stream ? out->stream->id : 0, out->priority,\n+                       out->blocked, out->length);\n+    }\n+\n+    cl = c->send_chain(c, cl, 0);\n+\n+    if (cl == NGX_CHAIN_ERROR) {\n+        goto error;\n+    }\n+\n+    clcf = ngx_http_get_module_loc_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_core_module);\n+\n+    if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {\n+        goto error;\n+    }\n+\n+    if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {\n+        if (ngx_tcp_push(c->fd) == -1) {\n+            ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n \" failed\");\n+            goto error;\n+        }\n+\n+        c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;\n+        tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;\n+\n+    } else {\n+        tcp_nodelay = 1;\n+    }\n+\n+    if (tcp_nodelay\n+        && clcf->tcp_nodelay\n+        && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET)\n+    {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"tcp_nodelay\");\n+\n+        if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,\n+                       (const void *) &tcp_nodelay, sizeof(int))\n+            == -1)\n+        {\n+#if (NGX_SOLARIS)\n+            /* Solaris returns EINVAL if a socket has been shut down */\n+            c->log_error = NGX_ERROR_IGNORE_EINVAL;\n+#endif\n+\n+            ngx_connection_error(c, ngx_socket_errno,\n+                                 \"setsockopt(TCP_NODELAY) failed\");\n+\n+            c->log_error = NGX_ERROR_INFO;\n+            goto error;\n+        }\n+\n+        c->tcp_nodelay = NGX_TCP_NODELAY_SET;\n+    }\n+\n+    if (cl) {\n+        ngx_add_timer(wev, clcf->send_timeout);\n+\n+    } else {\n+        if (wev->timer_set) {\n+            ngx_del_timer(wev);\n+        }\n+    }\n+\n+    for ( /* void */ ; out; out = fn) {\n+        fn = out->next;\n+\n+        if (out->handler(sc, out) != NGX_OK) {\n+            out->blocked = 1;\n+            out->priority = NGX_SPDY_HIGHEST_PRIORITY;\n+            break;\n+        }\n+\n+        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"spdy frame sent: %p sid:%ui bl:%d len:%uz\",\n+                       out, out->stream ? out->stream->id : 0,\n+                       out->blocked, out->length);\n+    }\n+\n+    frame = NULL;\n+\n+    for ( /* void */ ; out; out = fn) {\n+        fn = out->next;\n+        out->next = frame;\n+        frame = out;\n+    }\n+\n+    sc->last_out = frame;\n+\n+    return NGX_OK;\n+\n+error:\n+\n+    c->error = 1;\n+\n+    if (!sc->blocked) {\n+        ngx_post_event(wev, &ngx_posted_events);\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+\n+static void\n+ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc)\n+{\n+    ngx_connection_t          *c;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    if (sc->last_out || sc->processing) {\n+        return;\n+    }\n+\n+    c = sc->connection;\n+\n+    if (c->error) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    if (c->buffered) {\n+        return;\n+    }\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+    if (sc->incomplete) {\n+        ngx_add_timer(c->read, sscf->recv_timeout);\n+        return;\n+    }\n+\n+    if (ngx_terminate || ngx_exiting) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    ngx_destroy_pool(sc->pool);\n+\n+    sc->pool = NULL;\n+    sc->free_ctl_frames = NULL;\n+    sc->free_fake_connections = NULL;\n+\n+#if (NGX_HTTP_SSL)\n+    if (c->ssl) {\n+        ngx_ssl_free_buffer(c);\n+    }\n+#endif\n+\n+    c->destroyed = 1;\n+    c->idle = 1;\n+    ngx_reusable_connection(c, 1);\n+\n+    c->write->handler = ngx_http_empty_handler;\n+    c->read->handler = ngx_http_spdy_keepalive_handler;\n+\n+    if (c->write->timer_set) {\n+        ngx_del_timer(c->write);\n+    }\n+\n+    ngx_add_timer(c->read, sscf->keepalive_timeout);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_log_t  *log;\n+\n+    log = sc->connection->log;\n+    log->action = \"reading PROXY protocol\";\n+\n+    pos = ngx_proxy_protocol_read(sc->connection, pos, end);\n+\n+    log->action = \"processing SPDY\";\n+\n+    if (pos == NULL) {\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    uint32_t    head, flen;\n+    ngx_uint_t  type;\n+\n+    if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_head);\n+    }\n+\n+    head = ngx_spdy_frame_parse_uint32(pos);\n+\n+    pos += sizeof(uint32_t);\n+\n+    flen = ngx_spdy_frame_parse_uint32(pos);\n+\n+    sc->flags = ngx_spdy_frame_flags(flen);\n+    sc->length = ngx_spdy_frame_length(flen);\n+\n+    pos += sizeof(uint32_t);\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"process spdy frame head:%08XD f:%Xd l:%uz\",\n+                   head, sc->flags, sc->length);\n+\n+    if (ngx_spdy_ctl_frame_check(head)) {\n+        type = ngx_spdy_ctl_frame_type(head);\n+\n+        switch (type) {\n+\n+        case NGX_SPDY_SYN_STREAM:\n+            return ngx_http_spdy_state_syn_stream(sc, pos, end);\n+\n+        case NGX_SPDY_SYN_REPLY:\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client sent unexpected SYN_REPLY frame\");\n+            return ngx_http_spdy_state_protocol_error(sc);\n+\n+        case NGX_SPDY_RST_STREAM:\n+            return ngx_http_spdy_state_rst_stream(sc, pos, end);\n+\n+        case NGX_SPDY_SETTINGS:\n+            return ngx_http_spdy_state_settings(sc, pos, end);\n+\n+        case NGX_SPDY_PING:\n+            return ngx_http_spdy_state_ping(sc, pos, end);\n+\n+        case NGX_SPDY_GOAWAY:\n+            return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */\n+\n+        case NGX_SPDY_HEADERS:\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client sent unexpected HEADERS frame\");\n+            return ngx_http_spdy_state_protocol_error(sc);\n+\n+        case NGX_SPDY_WINDOW_UPDATE:\n+            return ngx_http_spdy_state_window_update(sc, pos, end);\n+\n+        default:\n+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"spdy control frame with unknown type %ui\", type);\n+            return ngx_http_spdy_state_skip(sc, pos, end);\n+        }\n+    }\n+\n+    if (ngx_spdy_data_frame_check(head)) {\n+        sc->stream = ngx_http_spdy_get_stream_by_id(sc, head);\n+        return ngx_http_spdy_state_data(sc, pos, end);\n+    }\n+\n+    ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                  \"client sent invalid frame\");\n+\n+    return ngx_http_spdy_state_protocol_error(sc);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_uint_t                 sid, prio;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    if (end - pos < NGX_SPDY_SYN_STREAM_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_syn_stream);\n+    }\n+\n+    if (sc->length <= NGX_SPDY_SYN_STREAM_SIZE) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent SYN_STREAM frame with incorrect length %uz\",\n+                      sc->length);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sc->length -= NGX_SPDY_SYN_STREAM_SIZE;\n+\n+    sid = ngx_spdy_frame_parse_sid(pos);\n+    prio = pos[8] >> 5;\n+\n+    pos += NGX_SPDY_SYN_STREAM_SIZE;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy SYN_STREAM frame sid:%ui prio:%ui\", sid, prio);\n+\n+    if (sid % 2 == 0 || sid <= sc->last_sid) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent SYN_STREAM frame \"\n+                      \"with invalid Stream-ID %ui\", sid);\n+\n+        stream = ngx_http_spdy_get_stream_by_id(sc, sid);\n+\n+        if (stream) {\n+            if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                               NGX_SPDY_PROTOCOL_ERROR)\n+                != NGX_OK)\n+            {\n+                return ngx_http_spdy_state_internal_error(sc);\n+            }\n+        }\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sc->last_sid = sid;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    if (sc->processing >= sscf->concurrent_streams) {\n+\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"concurrent streams exceeded %ui\", sc->processing);\n+\n+        if (ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM,\n+                                          prio)\n+            != NGX_OK)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+    }\n+\n+    stream = ngx_http_spdy_create_stream(sc, sid, prio);\n+    if (stream == NULL) {\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    stream->in_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0;\n+\n+    stream->request->request_length = NGX_SPDY_FRAME_HEADER_SIZE\n+                                      + NGX_SPDY_SYN_STREAM_SIZE\n+                                      + sc->length;\n+\n+    sc->stream = stream;\n+\n+    return ngx_http_spdy_state_headers(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    int                  z;\n+    size_t               size;\n+    ngx_buf_t           *buf;\n+    ngx_int_t            rc;\n+    ngx_http_request_t  *r;\n+\n+    size = end - pos;\n+\n+    if (size == 0) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_headers);\n+    }\n+\n+    if (size > sc->length) {\n+        size = sc->length;\n+    }\n+\n+    r = sc->stream->request;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"process spdy header block %uz of %uz\", size, sc->length);\n+\n+    buf = r->header_in;\n+\n+    sc->zstream_in.next_in = pos;\n+    sc->zstream_in.avail_in = size;\n+    sc->zstream_in.next_out = buf->last;\n+\n+    /* one byte is reserved for null-termination of the last header value */\n+    sc->zstream_in.avail_out = buf->end - buf->last - 1;\n+\n+    z = inflate(&sc->zstream_in, Z_NO_FLUSH);\n+\n+    if (z == Z_NEED_DICT) {\n+        z = inflateSetDictionary(&sc->zstream_in, ngx_http_spdy_dict,\n+                                 sizeof(ngx_http_spdy_dict));\n+\n+        if (z != Z_OK) {\n+            if (z == Z_DATA_ERROR) {\n+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                              \"client sent SYN_STREAM frame with header \"\n+                              \"block encoded using wrong dictionary: %ul\",\n+                              (u_long) sc->zstream_in.adler);\n+\n+                return ngx_http_spdy_state_protocol_error(sc);\n+            }\n+\n+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n+                          \"inflateSetDictionary() failed: %d\", z);\n+\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"spdy inflateSetDictionary(): %d\", z);\n+\n+        z = sc->zstream_in.avail_in ? inflate(&sc->zstream_in, Z_NO_FLUSH)\n+                                    : Z_OK;\n+    }\n+\n+    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n+                   sc->zstream_in.next_in, sc->zstream_in.next_out,\n+                   sc->zstream_in.avail_in, sc->zstream_in.avail_out,\n+                   z);\n+\n+    if (z != Z_OK) {\n+        return ngx_http_spdy_state_inflate_error(sc, z);\n+    }\n+\n+    sc->length -= sc->zstream_in.next_in - pos;\n+    pos = sc->zstream_in.next_in;\n+\n+    buf->last = sc->zstream_in.next_out;\n+\n+    if (r->headers_in.headers.part.elts == NULL) {\n+\n+        if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) {\n+\n+            if (sc->length == 0) {\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                               \"premature end of spdy header block\");\n+\n+                return ngx_http_spdy_state_headers_error(sc, pos, end);\n+            }\n+\n+            return ngx_http_spdy_state_save(sc, pos, end,\n+                                            ngx_http_spdy_state_headers);\n+        }\n+\n+        sc->entries = ngx_spdy_frame_parse_uint32(buf->pos);\n+\n+        buf->pos += NGX_SPDY_NV_NUM_SIZE;\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"spdy header block has %ui entries\",\n+                       sc->entries);\n+\n+        if (ngx_list_init(&r->headers_in.headers, r->pool, 20,\n+                          sizeof(ngx_table_elt_t))\n+            != NGX_OK)\n+        {\n+            ngx_http_spdy_close_stream(sc->stream,\n+                                       NGX_HTTP_INTERNAL_SERVER_ERROR);\n+            return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+        }\n+\n+        if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,\n+                           sizeof(ngx_table_elt_t *))\n+            != NGX_OK)\n+        {\n+            ngx_http_spdy_close_stream(sc->stream,\n+                                       NGX_HTTP_INTERNAL_SERVER_ERROR);\n+            return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+        }\n+    }\n+\n+    while (sc->entries) {\n+\n+        rc = ngx_http_spdy_parse_header(r);\n+\n+        switch (rc) {\n+\n+        case NGX_DONE:\n+            sc->entries--;\n+\n+        case NGX_OK:\n+            break;\n+\n+        case NGX_AGAIN:\n+\n+            if (sc->zstream_in.avail_in) {\n+\n+                rc = ngx_http_spdy_alloc_large_header_buffer(r);\n+\n+                if (rc == NGX_DECLINED) {\n+                    ngx_http_finalize_request(r,\n+                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);\n+                    return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+                }\n+\n+                if (rc != NGX_OK) {\n+                    ngx_http_spdy_close_stream(sc->stream,\n+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n+                    return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+                }\n+\n+                /* null-terminate the last processed header name or value */\n+                *buf->pos = '\\0';\n+\n+                buf = r->header_in;\n+\n+                sc->zstream_in.next_out = buf->last;\n+\n+                /* one byte is reserved for null-termination */\n+                sc->zstream_in.avail_out = buf->end - buf->last - 1;\n+\n+                z = inflate(&sc->zstream_in, Z_NO_FLUSH);\n+\n+                ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                           \"spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n+                           sc->zstream_in.next_in, sc->zstream_in.next_out,\n+                           sc->zstream_in.avail_in, sc->zstream_in.avail_out,\n+                           z);\n+\n+                if (z != Z_OK) {\n+                    return ngx_http_spdy_state_inflate_error(sc, z);\n+                }\n+\n+                sc->length -= sc->zstream_in.next_in - pos;\n+                pos = sc->zstream_in.next_in;\n+\n+                buf->last = sc->zstream_in.next_out;\n+\n+                continue;\n+            }\n+\n+            if (sc->length == 0) {\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                               \"premature end of spdy header block\");\n+\n+                return ngx_http_spdy_state_headers_error(sc, pos, end);\n+            }\n+\n+            return ngx_http_spdy_state_save(sc, pos, end,\n+                                            ngx_http_spdy_state_headers);\n+\n+        case NGX_HTTP_PARSE_INVALID_HEADER:\n+            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+            return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+\n+        default: /* NGX_ERROR */\n+            return ngx_http_spdy_state_headers_error(sc, pos, end);\n+        }\n+\n+        /* a header line has been parsed successfully */\n+\n+        rc = ngx_http_spdy_handle_request_header(r);\n+\n+        if (rc != NGX_OK) {\n+            if (rc == NGX_HTTP_PARSE_INVALID_HEADER) {\n+                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+                return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+            }\n+\n+            if (rc != NGX_ABORT) {\n+                ngx_http_spdy_close_stream(sc->stream,\n+                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n+            }\n+\n+            return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+        }\n+    }\n+\n+    if (buf->pos != buf->last || sc->zstream_in.avail_in) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"incorrect number of spdy header block entries\");\n+\n+        return ngx_http_spdy_state_headers_error(sc, pos, end);\n+    }\n+\n+    if (sc->length) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_headers);\n+    }\n+\n+    /* null-terminate the last header value */\n+    *buf->pos = '\\0';\n+\n+    ngx_http_spdy_run_request(r);\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    int     n;\n+    size_t  size;\n+    u_char  buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE];\n+\n+    if (sc->length == 0) {\n+        return ngx_http_spdy_state_complete(sc, pos, end);\n+    }\n+\n+    size = end - pos;\n+\n+    if (size == 0) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_headers_skip);\n+    }\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy header block skip %uz of %uz\", size, sc->length);\n+\n+    sc->zstream_in.next_in = pos;\n+    sc->zstream_in.avail_in = (size < sc->length) ? size : sc->length;\n+\n+    while (sc->zstream_in.avail_in) {\n+        sc->zstream_in.next_out = buffer;\n+        sc->zstream_in.avail_out = NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE;\n+\n+        n = inflate(&sc->zstream_in, Z_NO_FLUSH);\n+\n+        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n+                       sc->zstream_in.next_in, sc->zstream_in.next_out,\n+                       sc->zstream_in.avail_in, sc->zstream_in.avail_out,\n+                       n);\n+\n+        if (n != Z_OK) {\n+            return ngx_http_spdy_state_inflate_error(sc, n);\n+        }\n+    }\n+\n+    pos = sc->zstream_in.next_in;\n+\n+    if (size < sc->length) {\n+        sc->length -= size;\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_headers_skip);\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    stream = sc->stream;\n+\n+    ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                  \"client sent SYN_STREAM frame for stream %ui \"\n+                  \"with invalid header block\", stream->id);\n+\n+    if (ngx_http_spdy_send_rst_stream(sc, stream->id, NGX_SPDY_PROTOCOL_ERROR,\n+                                      stream->priority)\n+        != NGX_OK)\n+    {\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    stream->out_closed = 1;\n+\n+    ngx_http_spdy_close_stream(stream, NGX_HTTP_BAD_REQUEST);\n+\n+    return ngx_http_spdy_state_headers_skip(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    size_t                   delta;\n+    ngx_uint_t               sid;\n+    ngx_event_t             *wev;\n+    ngx_queue_t             *q;\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    if (end - pos < NGX_SPDY_WINDOW_UPDATE_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_window_update);\n+    }\n+\n+    if (sc->length != NGX_SPDY_WINDOW_UPDATE_SIZE) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent WINDOW_UPDATE frame \"\n+                      \"with incorrect length %uz\", sc->length);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sid = ngx_spdy_frame_parse_sid(pos);\n+\n+    pos += NGX_SPDY_SID_SIZE;\n+\n+    delta = ngx_spdy_frame_parse_delta(pos);\n+\n+    pos += NGX_SPDY_DELTA_SIZE;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy WINDOW_UPDATE sid:%ui delta:%uz\", sid, delta);\n+\n+    if (sid) {\n+        stream = ngx_http_spdy_get_stream_by_id(sc, sid);\n+\n+        if (stream == NULL) {\n+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"unknown spdy stream\");\n+\n+            return ngx_http_spdy_state_complete(sc, pos, end);\n+        }\n+\n+        if (stream->send_window > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta)) {\n+\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client violated flow control for stream %ui: \"\n+                          \"received WINDOW_UPDATE frame with delta %uz \"\n+                          \"not allowed for window %z\",\n+                          sid, delta, stream->send_window);\n+\n+            if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                               NGX_SPDY_FLOW_CONTROL_ERROR)\n+                == NGX_ERROR)\n+            {\n+                return ngx_http_spdy_state_internal_error(sc);\n+            }\n+\n+            return ngx_http_spdy_state_complete(sc, pos, end);\n+        }\n+\n+        stream->send_window += delta;\n+\n+        if (stream->exhausted) {\n+            stream->exhausted = 0;\n+\n+            wev = stream->request->connection->write;\n+\n+            if (!wev->timer_set) {\n+                wev->delayed = 0;\n+                wev->handler(wev);\n+            }\n+        }\n+\n+    } else {\n+        sc->send_window += delta;\n+\n+        if (sc->send_window > NGX_SPDY_MAX_WINDOW) {\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client violated connection flow control: \"\n+                          \"received WINDOW_UPDATE frame with delta %uz \"\n+                          \"not allowed for window %uz\",\n+                          delta, sc->send_window);\n+\n+            return ngx_http_spdy_state_protocol_error(sc);\n+        }\n+\n+        while (!ngx_queue_empty(&sc->waiting)) {\n+            q = ngx_queue_head(&sc->waiting);\n+\n+            ngx_queue_remove(q);\n+\n+            stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);\n+\n+            stream->handled = 0;\n+\n+            wev = stream->request->connection->write;\n+\n+            if (!wev->timer_set) {\n+                wev->delayed = 0;\n+                wev->handler(wev);\n+\n+                if (sc->send_window == 0) {\n+                    break;\n+                }\n+            }\n+        }\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy DATA frame\");\n+\n+    if (sc->length > sc->recv_window) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client violated connection flow control: \"\n+                      \"received DATA frame length %uz, available window %uz\",\n+                      sc->length, sc->recv_window);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sc->recv_window -= sc->length;\n+\n+    if (sc->recv_window < NGX_SPDY_MAX_WINDOW / 4) {\n+\n+        if (ngx_http_spdy_send_window_update(sc, 0,\n+                                             NGX_SPDY_MAX_WINDOW\n+                                             - sc->recv_window)\n+            == NGX_ERROR)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        sc->recv_window = NGX_SPDY_MAX_WINDOW;\n+    }\n+\n+    stream = sc->stream;\n+\n+    if (stream == NULL) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"unknown spdy stream\");\n+\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    if (sc->length > stream->recv_window) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client violated flow control for stream %ui: \"\n+                      \"received DATA frame length %uz, available window %uz\",\n+                      stream->id, sc->length, stream->recv_window);\n+\n+        if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                           NGX_SPDY_FLOW_CONTROL_ERROR)\n+            == NGX_ERROR)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    stream->recv_window -= sc->length;\n+\n+    if (stream->recv_window < NGX_SPDY_STREAM_WINDOW / 4) {\n+\n+        if (ngx_http_spdy_send_window_update(sc, stream->id,\n+                                             NGX_SPDY_STREAM_WINDOW\n+                                             - stream->recv_window)\n+            == NGX_ERROR)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        stream->recv_window = NGX_SPDY_STREAM_WINDOW;\n+    }\n+\n+    if (stream->in_closed) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent DATA frame for half-closed stream %ui\",\n+                      stream->id);\n+\n+        if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                           NGX_SPDY_STREAM_ALREADY_CLOSED)\n+            == NGX_ERROR)\n+        {\n+            return ngx_http_spdy_state_internal_error(sc);\n+        }\n+\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    return ngx_http_spdy_state_read_data(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    size_t                     size;\n+    ssize_t                    n;\n+    ngx_buf_t                 *buf;\n+    ngx_int_t                  rc;\n+    ngx_temp_file_t           *tf;\n+    ngx_http_request_t        *r;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_request_body_t   *rb;\n+    ngx_http_core_loc_conf_t  *clcf;\n+\n+    stream = sc->stream;\n+\n+    if (stream == NULL) {\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    if (stream->skip_data) {\n+\n+        if (sc->flags & NGX_SPDY_FLAG_FIN) {\n+            stream->in_closed = 1;\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"skipping spdy DATA frame, reason: %d\",\n+                       stream->skip_data);\n+\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    size = end - pos;\n+\n+    if (size > sc->length) {\n+        size = sc->length;\n+    }\n+\n+    r = stream->request;\n+\n+    if (r->request_body == NULL\n+        && ngx_http_spdy_init_request_body(r) != NGX_OK)\n+    {\n+        stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;\n+        return ngx_http_spdy_state_skip(sc, pos, end);\n+    }\n+\n+    rb = r->request_body;\n+    tf = rb->temp_file;\n+    buf = rb->buf;\n+\n+    if (size) {\n+        rb->rest += size;\n+\n+        if (r->headers_in.content_length_n != -1\n+            && r->headers_in.content_length_n < rb->rest)\n+        {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client intended to send body data \"\n+                          \"larger than declared\");\n+\n+            stream->skip_data = NGX_SPDY_DATA_ERROR;\n+            goto error;\n+\n+        } else {\n+            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+            if (clcf->client_max_body_size\n+                && clcf->client_max_body_size < rb->rest)\n+            {\n+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n+                              \"client intended to send \"\n+                              \"too large chunked body: %O bytes\", rb->rest);\n+\n+                stream->skip_data = NGX_SPDY_DATA_ERROR;\n+                goto error;\n+            }\n+        }\n+\n+        sc->length -= size;\n+\n+        if (tf) {\n+            buf->start = pos;\n+            buf->pos = pos;\n+\n+            pos += size;\n+\n+            buf->end = pos;\n+            buf->last = pos;\n+\n+            n = ngx_write_chain_to_temp_file(tf, rb->bufs);\n+\n+            /* TODO: n == 0 or not complete and level event */\n+\n+            if (n == NGX_ERROR) {\n+                stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;\n+                goto error;\n+            }\n+\n+            tf->offset += n;\n+\n+        } else {\n+            buf->last = ngx_cpymem(buf->last, pos, size);\n+            pos += size;\n+        }\n+\n+        r->request_length += size;\n+    }\n+\n+    if (sc->length) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_read_data);\n+    }\n+\n+    if (sc->flags & NGX_SPDY_FLAG_FIN) {\n+\n+        stream->in_closed = 1;\n+\n+        if (r->headers_in.content_length_n < 0) {\n+            r->headers_in.content_length_n = rb->rest;\n+\n+        } else if (r->headers_in.content_length_n != rb->rest) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client prematurely closed stream: \"\n+                          \"only %O out of %O bytes of request body received\",\n+                          rb->rest, r->headers_in.content_length_n);\n+\n+            stream->skip_data = NGX_SPDY_DATA_ERROR;\n+            goto error;\n+        }\n+\n+        if (tf) {\n+            ngx_memzero(buf, sizeof(ngx_buf_t));\n+\n+            buf->in_file = 1;\n+            buf->file_last = tf->file.offset;\n+            buf->file = &tf->file;\n+\n+            rb->buf = NULL;\n+        }\n+\n+        if (rb->post_handler) {\n+            r->read_event_handler = ngx_http_block_reading;\n+            rb->post_handler(r);\n+        }\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+\n+error:\n+\n+    if (rb->post_handler) {\n+\n+        if (stream->skip_data == NGX_SPDY_DATA_ERROR) {\n+            rc = (r->headers_in.content_length_n == -1)\n+                 ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE\n+                 : NGX_HTTP_BAD_REQUEST;\n+\n+        } else {\n+            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n+        }\n+\n+        ngx_http_finalize_request(r, rc);\n+    }\n+\n+    return ngx_http_spdy_state_skip(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_uint_t               sid, status;\n+    ngx_event_t             *ev;\n+    ngx_connection_t        *fc;\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    if (end - pos < NGX_SPDY_RST_STREAM_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_rst_stream);\n+    }\n+\n+    if (sc->length != NGX_SPDY_RST_STREAM_SIZE) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent RST_STREAM frame with incorrect length %uz\",\n+                      sc->length);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    sid = ngx_spdy_frame_parse_sid(pos);\n+\n+    pos += NGX_SPDY_SID_SIZE;\n+\n+    status = ngx_spdy_frame_parse_uint32(pos);\n+\n+    pos += sizeof(uint32_t);\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy RST_STREAM sid:%ui st:%ui\", sid, status);\n+\n+    stream = ngx_http_spdy_get_stream_by_id(sc, sid);\n+\n+    if (stream == NULL) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"unknown spdy stream\");\n+\n+        return ngx_http_spdy_state_complete(sc, pos, end);\n+    }\n+\n+    stream->in_closed = 1;\n+    stream->out_closed = 1;\n+\n+    fc = stream->request->connection;\n+    fc->error = 1;\n+\n+    switch (status) {\n+\n+    case NGX_SPDY_CANCEL:\n+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n+                      \"client canceled stream %ui\", sid);\n+        break;\n+\n+    case NGX_SPDY_INTERNAL_ERROR:\n+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n+                      \"client terminated stream %ui due to internal error\",\n+                      sid);\n+        break;\n+\n+    default:\n+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n+                      \"client terminated stream %ui with status %ui\",\n+                      sid, status);\n+        break;\n+    }\n+\n+    ev = fc->read;\n+    ev->handler(ev);\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    if (end - pos < NGX_SPDY_PING_SIZE) {\n+        return ngx_http_spdy_state_save(sc, pos, end,\n+                                        ngx_http_spdy_state_ping);\n+    }\n+\n+    if (sc->length != NGX_SPDY_PING_SIZE) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent PING frame with incorrect length %uz\",\n+                      sc->length);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy PING frame\");\n+\n+    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_PING_SIZE,\n+                                        NGX_SPDY_HIGHEST_PRIORITY);\n+    if (frame == NULL) {\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    buf = frame->first->buf;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_PING);\n+    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_PING_SIZE);\n+\n+    p = ngx_cpymem(p, pos, NGX_SPDY_PING_SIZE);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    pos += NGX_SPDY_PING_SIZE;\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    size_t  size;\n+\n+    size = end - pos;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy frame skip %uz of %uz\", size, sc->length);\n+\n+    if (size < sc->length) {\n+        sc->length -= size;\n+        return ngx_http_spdy_state_save(sc, end, end,\n+                                        ngx_http_spdy_state_skip);\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos + sc->length, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_uint_t  fid, val;\n+\n+    if (sc->entries == 0) {\n+\n+        if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) {\n+            return ngx_http_spdy_state_save(sc, pos, end,\n+                                            ngx_http_spdy_state_settings);\n+        }\n+\n+        sc->entries = ngx_spdy_frame_parse_uint32(pos);\n+\n+        pos += NGX_SPDY_SETTINGS_NUM_SIZE;\n+        sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE;\n+\n+        if (sc->length < sc->entries * NGX_SPDY_SETTINGS_PAIR_SIZE) {\n+            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                          \"client sent SETTINGS frame with incorrect \"\n+                          \"length %uz or number of entries %ui\",\n+                          sc->length, sc->entries);\n+\n+            return ngx_http_spdy_state_protocol_error(sc);\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"spdy SETTINGS frame has %ui entries\", sc->entries);\n+    }\n+\n+    while (sc->entries) {\n+        if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) {\n+            return ngx_http_spdy_state_save(sc, pos, end,\n+                                            ngx_http_spdy_state_settings);\n+        }\n+\n+        sc->entries--;\n+        sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE;\n+\n+        fid = ngx_spdy_frame_parse_uint32(pos);\n+\n+        pos += NGX_SPDY_SETTINGS_FID_SIZE;\n+\n+        val = ngx_spdy_frame_parse_uint32(pos);\n+\n+        pos += NGX_SPDY_SETTINGS_VAL_SIZE;\n+\n+        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                       \"spdy SETTINGS entry fl:%ui id:%ui val:%ui\",\n+                       ngx_spdy_frame_flags(fid), ngx_spdy_frame_id(fid), val);\n+\n+        if (ngx_spdy_frame_flags(fid) == NGX_SPDY_SETTINGS_FLAG_PERSISTED) {\n+            continue;\n+        }\n+\n+        switch (ngx_spdy_frame_id(fid)) {\n+\n+        case NGX_SPDY_SETTINGS_INIT_WINDOW:\n+\n+            if (val > NGX_SPDY_MAX_WINDOW) {\n+                ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                              \"client sent SETTINGS frame with \"\n+                              \"incorrect INIT_WINDOW value: %ui\", val);\n+\n+                return ngx_http_spdy_state_protocol_error(sc);\n+            }\n+\n+            if (ngx_http_spdy_adjust_windows(sc, val - sc->init_window)\n+                != NGX_OK)\n+            {\n+                return ngx_http_spdy_state_internal_error(sc);\n+            }\n+\n+            sc->init_window = val;\n+\n+            continue;\n+        }\n+    }\n+\n+    return ngx_http_spdy_state_complete(sc, pos, end);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, u_char *pos,\n+    u_char *end)\n+{\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy frame complete pos:%p end:%p\", pos, end);\n+\n+    if (pos > end) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,\n+                      \"receive buffer overrun\");\n+\n+        ngx_debug_point();\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    sc->handler = ngx_http_spdy_state_head;\n+    sc->stream = NULL;\n+\n+    return pos;\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler)\n+{\n+    size_t  size;\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy frame state save pos:%p end:%p handler:%p\",\n+                   pos, end, handler);\n+\n+    size = end - pos;\n+\n+    if (size > NGX_SPDY_STATE_BUFFER_SIZE) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,\n+                      \"state buffer overflow: %uz bytes required\", size);\n+\n+        ngx_debug_point();\n+        return ngx_http_spdy_state_internal_error(sc);\n+    }\n+\n+    ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE);\n+\n+    sc->buffer_used = size;\n+    sc->handler = handler;\n+    sc->incomplete = 1;\n+\n+    return end;\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_inflate_error(ngx_http_spdy_connection_t *sc, int rc)\n+{\n+    if (rc == Z_DATA_ERROR || rc == Z_STREAM_END) {\n+        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,\n+                      \"client sent SYN_STREAM frame with \"\n+                      \"corrupted header block, inflate() failed: %d\", rc);\n+\n+        return ngx_http_spdy_state_protocol_error(sc);\n+    }\n+\n+    ngx_log_error(NGX_LOG_ERR, sc->connection->log, 0,\n+                  \"inflate() failed: %d\", rc);\n+\n+    return ngx_http_spdy_state_internal_error(sc);\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_protocol_error(ngx_http_spdy_connection_t *sc)\n+{\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy state protocol error\");\n+\n+    if (sc->stream) {\n+        sc->stream->out_closed = 1;\n+        ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST);\n+    }\n+\n+    ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+\n+    return NULL;\n+}\n+\n+\n+static u_char *\n+ngx_http_spdy_state_internal_error(ngx_http_spdy_connection_t *sc)\n+{\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy state internal error\");\n+\n+    if (sc->stream) {\n+        sc->stream->out_closed = 1;\n+        ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+    }\n+\n+    ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+\n+    return NULL;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_send_window_update(ngx_http_spdy_connection_t *sc, ngx_uint_t sid,\n+    ngx_uint_t delta)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy send WINDOW_UPDATE sid:%ui delta:%ui\", sid, delta);\n+\n+    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_WINDOW_UPDATE_SIZE,\n+                                        NGX_SPDY_HIGHEST_PRIORITY);\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf = frame->first->buf;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_WINDOW_UPDATE);\n+    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_WINDOW_UPDATE_SIZE);\n+\n+    p = ngx_spdy_frame_write_sid(p, sid);\n+    p = ngx_spdy_frame_aligned_write_uint32(p, delta);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t sid,\n+    ngx_uint_t status, ngx_uint_t priority)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    if (sc->connection->error) {\n+        return NGX_OK;\n+    }\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy send RST_STREAM sid:%ui st:%ui\", sid, status);\n+\n+    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_RST_STREAM_SIZE,\n+                                        priority);\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf = frame->first->buf;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_RST_STREAM);\n+    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_RST_STREAM_SIZE);\n+\n+    p = ngx_spdy_frame_write_sid(p, sid);\n+    p = ngx_spdy_frame_aligned_write_uint32(p, status);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    return NGX_OK;\n+}\n+\n+\n+#if 0\n+static ngx_int_t\n+ngx_http_spdy_send_goaway(ngx_http_spdy_connection_t *sc)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy send GOAWAY sid:%ui\", sc->last_sid);\n+\n+    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_GOAWAY_SIZE,\n+                                        NGX_SPDY_HIGHEST_PRIORITY);\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf = frame->first->buf;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_GOAWAY);\n+    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_GOAWAY_SIZE);\n+\n+    p = ngx_spdy_frame_write_sid(p, sc->last_sid);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    return NGX_OK;\n+}\n+#endif\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_chain_t                *cl;\n+    ngx_http_spdy_srv_conf_t   *sscf;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy send SETTINGS frame\");\n+\n+    frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t));\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    cl = ngx_alloc_chain_link(sc->pool);\n+    if (cl == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf = ngx_create_temp_buf(sc->pool, NGX_SPDY_FRAME_HEADER_SIZE\n+                                        + NGX_SPDY_SETTINGS_NUM_SIZE\n+                                        + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE);\n+    if (buf == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    buf->last_buf = 1;\n+\n+    cl->buf = buf;\n+    cl->next = NULL;\n+\n+    frame->first = cl;\n+    frame->last = cl;\n+    frame->handler = ngx_http_spdy_settings_frame_handler;\n+    frame->stream = NULL;\n+#if (NGX_DEBUG)\n+    frame->length = NGX_SPDY_SETTINGS_NUM_SIZE\n+                    + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE;\n+#endif\n+    frame->priority = NGX_SPDY_HIGHEST_PRIORITY;\n+    frame->blocked = 0;\n+\n+    p = buf->pos;\n+\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_SETTINGS);\n+    p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_CLEAR_SETTINGS,\n+                                           NGX_SPDY_SETTINGS_NUM_SIZE\n+                                           + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE);\n+\n+    p = ngx_spdy_frame_aligned_write_uint32(p, 2);\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_MAX_STREAMS);\n+    p = ngx_spdy_frame_aligned_write_uint32(p, sscf->concurrent_streams);\n+\n+    p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_INIT_WINDOW);\n+    p = ngx_spdy_frame_aligned_write_uint32(p, NGX_SPDY_STREAM_WINDOW);\n+\n+    buf->last = p;\n+\n+    ngx_http_spdy_queue_frame(sc, frame);\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_buf_t  *buf;\n+\n+    buf = frame->first->buf;\n+\n+    if (buf->pos != buf->last) {\n+        return NGX_AGAIN;\n+    }\n+\n+    ngx_free_chain(sc->pool, frame->first);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_http_spdy_out_frame_t *\n+ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t length,\n+    ngx_uint_t priority)\n+{\n+    ngx_chain_t                *cl;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+    frame = sc->free_ctl_frames;\n+\n+    if (frame) {\n+        sc->free_ctl_frames = frame->next;\n+\n+        cl = frame->first;\n+        cl->buf->pos = cl->buf->start;\n+\n+    } else {\n+        frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t));\n+        if (frame == NULL) {\n+            return NULL;\n+        }\n+\n+        cl = ngx_alloc_chain_link(sc->pool);\n+        if (cl == NULL) {\n+            return NULL;\n+        }\n+\n+        cl->buf = ngx_create_temp_buf(sc->pool,\n+                                      NGX_SPDY_CTL_FRAME_BUFFER_SIZE);\n+        if (cl->buf == NULL) {\n+            return NULL;\n+        }\n+\n+        cl->buf->last_buf = 1;\n+\n+        frame->first = cl;\n+        frame->last = cl;\n+        frame->handler = ngx_http_spdy_ctl_frame_handler;\n+        frame->stream = NULL;\n+    }\n+\n+#if (NGX_DEBUG)\n+    if (length > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0,\n+                      \"requested control frame is too large: %uz\", length);\n+        return NULL;\n+    }\n+\n+    frame->length = length;\n+#endif\n+\n+    frame->priority = priority;\n+    frame->blocked = 0;\n+\n+    return frame;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_buf_t  *buf;\n+\n+    buf = frame->first->buf;\n+\n+    if (buf->pos != buf->last) {\n+        return NGX_AGAIN;\n+    }\n+\n+    frame->next = sc->free_ctl_frames;\n+    sc->free_ctl_frames = frame;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_http_spdy_stream_t *\n+ngx_http_spdy_create_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t id,\n+    ngx_uint_t priority)\n+{\n+    ngx_log_t                 *log;\n+    ngx_uint_t                 index;\n+    ngx_event_t               *rev, *wev;\n+    ngx_connection_t          *fc;\n+    ngx_http_log_ctx_t        *ctx;\n+    ngx_http_request_t        *r;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_core_srv_conf_t  *cscf;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    fc = sc->free_fake_connections;\n+\n+    if (fc) {\n+        sc->free_fake_connections = fc->data;\n+\n+        rev = fc->read;\n+        wev = fc->write;\n+        log = fc->log;\n+        ctx = log->data;\n+\n+    } else {\n+        fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t));\n+        if (fc == NULL) {\n+            return NULL;\n+        }\n+\n+        rev = ngx_palloc(sc->pool, sizeof(ngx_event_t));\n+        if (rev == NULL) {\n+            return NULL;\n+        }\n+\n+        wev = ngx_palloc(sc->pool, sizeof(ngx_event_t));\n+        if (wev == NULL) {\n+            return NULL;\n+        }\n+\n+        log = ngx_palloc(sc->pool, sizeof(ngx_log_t));\n+        if (log == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx = ngx_palloc(sc->pool, sizeof(ngx_http_log_ctx_t));\n+        if (ctx == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx->connection = fc;\n+        ctx->request = NULL;\n+    }\n+\n+    ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t));\n+\n+    log->data = ctx;\n+\n+    ngx_memzero(rev, sizeof(ngx_event_t));\n+\n+    rev->data = fc;\n+    rev->ready = 1;\n+    rev->handler = ngx_http_spdy_close_stream_handler;\n+    rev->log = log;\n+\n+    ngx_memcpy(wev, rev, sizeof(ngx_event_t));\n+\n+    wev->write = 1;\n+\n+    ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t));\n+\n+    fc->data = sc->http_connection;\n+    fc->read = rev;\n+    fc->write = wev;\n+    fc->sent = 0;\n+    fc->log = log;\n+    fc->buffered = 0;\n+    fc->sndlowat = 1;\n+    fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n+\n+    r = ngx_http_create_request(fc);\n+    if (r == NULL) {\n+        return NULL;\n+    }\n+\n+    r->valid_location = 1;\n+\n+    fc->data = r;\n+    sc->connection->requests++;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    r->header_in = ngx_create_temp_buf(r->pool,\n+                                       cscf->client_header_buffer_size);\n+    if (r->header_in == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n+\n+    stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t));\n+    if (stream == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->spdy_stream = stream;\n+\n+    stream->id = id;\n+    stream->request = r;\n+    stream->connection = sc;\n+\n+    stream->send_window = sc->init_window;\n+    stream->recv_window = NGX_SPDY_STREAM_WINDOW;\n+\n+    stream->priority = priority;\n+\n+    sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module);\n+\n+    index = ngx_http_spdy_stream_index(sscf, id);\n+\n+    stream->index = sc->streams_index[index];\n+    sc->streams_index[index] = stream;\n+\n+    sc->processing++;\n+\n+    return stream;\n+}\n+\n+\n+static ngx_http_spdy_stream_t *\n+ngx_http_spdy_get_stream_by_id(ngx_http_spdy_connection_t *sc,\n+    ngx_uint_t sid)\n+{\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    stream = sc->streams_index[ngx_http_spdy_stream_index(sscf, sid)];\n+\n+    while (stream) {\n+        if (stream->id == sid) {\n+            return stream;\n+        }\n+\n+        stream = stream->index;\n+    }\n+\n+    return NULL;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_header(ngx_http_request_t *r)\n+{\n+    u_char                     *p, *end, ch;\n+    ngx_uint_t                  hash;\n+    ngx_http_core_srv_conf_t   *cscf;\n+\n+    enum {\n+        sw_name_len = 0,\n+        sw_name,\n+        sw_value_len,\n+        sw_value\n+    } state;\n+\n+    state = r->state;\n+\n+    p = r->header_in->pos;\n+    end = r->header_in->last;\n+\n+    switch (state) {\n+\n+    case sw_name_len:\n+\n+        if (end - p < NGX_SPDY_NV_NLEN_SIZE) {\n+            return NGX_AGAIN;\n+        }\n+\n+        r->lowcase_index = ngx_spdy_frame_parse_uint32(p);\n+\n+        if (r->lowcase_index == 0) {\n+            return NGX_ERROR;\n+        }\n+\n+        /* null-terminate the previous header value */\n+        *p = '\\0';\n+\n+        p += NGX_SPDY_NV_NLEN_SIZE;\n+\n+        r->invalid_header = 0;\n+\n+        state = sw_name;\n+\n+        /* fall through */\n+\n+    case sw_name:\n+\n+        if ((ngx_uint_t) (end - p) < r->lowcase_index) {\n+            break;\n+        }\n+\n+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+        r->header_name_start = p;\n+        r->header_name_end = p + r->lowcase_index;\n+\n+        if (p[0] == ':') {\n+            p++;\n+        }\n+\n+        hash = 0;\n+\n+        for ( /* void */ ; p != r->header_name_end; p++) {\n+\n+            ch = *p;\n+\n+            hash = ngx_hash(hash, ch);\n+\n+            if ((ch >= 'a' && ch <= 'z')\n+                || (ch == '-')\n+                || (ch >= '0' && ch <= '9')\n+                || (ch == '_' && cscf->underscores_in_headers))\n+            {\n+                continue;\n+            }\n+\n+            switch (ch) {\n+            case '\\0':\n+            case LF:\n+            case CR:\n+            case ':':\n+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                              \"client sent invalid header name: \\\"%*s\\\"\",\n+                              r->lowcase_index, r->header_name_start);\n+\n+                return NGX_HTTP_PARSE_INVALID_HEADER;\n+            }\n+\n+            if (ch >= 'A' && ch <= 'Z') {\n+                return NGX_ERROR;\n+            }\n+\n+            r->invalid_header = 1;\n+        }\n+\n+        r->header_hash = hash;\n+\n+        state = sw_value_len;\n+\n+        /* fall through */\n+\n+    case sw_value_len:\n+\n+        if (end - p < NGX_SPDY_NV_VLEN_SIZE) {\n+            break;\n+        }\n+\n+        r->lowcase_index = ngx_spdy_frame_parse_uint32(p);\n+\n+        /* null-terminate header name */\n+        *p = '\\0';\n+\n+        p += NGX_SPDY_NV_VLEN_SIZE;\n+\n+        state = sw_value;\n+\n+        /* fall through */\n+\n+    case sw_value:\n+\n+        if ((ngx_uint_t) (end - p) < r->lowcase_index) {\n+            break;\n+        }\n+\n+        r->header_start = p;\n+\n+        while (r->lowcase_index--) {\n+            ch = *p;\n+\n+            if (ch == '\\0') {\n+\n+                if (p == r->header_start) {\n+                    return NGX_ERROR;\n+                }\n+\n+                r->header_end = p;\n+                r->header_in->pos = p + 1;\n+\n+                r->state = sw_value;\n+\n+                return NGX_OK;\n+            }\n+\n+            if (ch == CR || ch == LF) {\n+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                              \"client sent header \\\"%*s\\\" with \"\n+                              \"invalid value: \\\"%*s\\\\%c...\\\"\",\n+                              r->header_name_end - r->header_name_start,\n+                              r->header_name_start,\n+                              p - r->header_start,\n+                              r->header_start,\n+                              ch == CR ? 'r' : 'n');\n+\n+                return NGX_HTTP_PARSE_INVALID_HEADER;\n+            }\n+\n+            p++;\n+        }\n+\n+        r->header_end = p;\n+        r->header_in->pos = p;\n+\n+        r->state = 0;\n+\n+        return NGX_DONE;\n+    }\n+\n+    r->header_in->pos = p;\n+    r->state = state;\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r)\n+{\n+    u_char                    *old, *new, *p;\n+    size_t                     rest;\n+    ngx_buf_t                 *buf;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy alloc large header buffer\");\n+\n+    stream = r->spdy_stream;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    if (stream->header_buffers\n+        == (ngx_uint_t) cscf->large_client_header_buffers.num)\n+    {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent too large request\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    rest = r->header_in->last - r->header_in->pos;\n+\n+    /*\n+     * One more byte is needed for null-termination\n+     * and another one for further progress.\n+     */\n+    if (rest > cscf->large_client_header_buffers.size - 2) {\n+        p = r->header_in->pos;\n+\n+        if (rest > NGX_MAX_ERROR_STR - 300) {\n+            rest = NGX_MAX_ERROR_STR - 300;\n+        }\n+\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent too long header name or value: \\\"%*s...\\\"\",\n+                      rest, p);\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    buf = ngx_create_temp_buf(r->pool, cscf->large_client_header_buffers.size);\n+    if (buf == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy large header alloc: %p %uz\",\n+                   buf->pos, buf->end - buf->last);\n+\n+    old = r->header_in->pos;\n+    new = buf->pos;\n+\n+    if (rest) {\n+        buf->last = ngx_cpymem(new, old, rest);\n+    }\n+\n+    r->header_in = buf;\n+\n+    stream->header_buffers++;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_handle_request_header(ngx_http_request_t *r)\n+{\n+    ngx_uint_t                       i;\n+    ngx_table_elt_t                 *h;\n+    ngx_http_core_srv_conf_t        *cscf;\n+    ngx_http_spdy_request_header_t  *sh;\n+\n+    if (r->invalid_header) {\n+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+        if (cscf->ignore_invalid_headers) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid header: \\\"%*s\\\"\",\n+                          r->header_end - r->header_name_start,\n+                          r->header_name_start);\n+            return NGX_OK;\n+        }\n+\n+    }\n+\n+    if (r->header_name_start[0] == ':') {\n+        r->header_name_start++;\n+\n+        for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {\n+            sh = &ngx_http_spdy_request_headers[i];\n+\n+            if (sh->hash != r->header_hash\n+                || sh->len != r->header_name_end - r->header_name_start\n+                || ngx_strncmp(sh->header, r->header_name_start, sh->len) != 0)\n+            {\n+                continue;\n+            }\n+\n+            return sh->handler(r);\n+        }\n+\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent invalid header name: \\\":%*s\\\"\",\n+                      r->header_end - r->header_name_start,\n+                      r->header_name_start);\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    h->hash = r->header_hash;\n+\n+    h->key.len = r->header_name_end - r->header_name_start;\n+    h->key.data = r->header_name_start;\n+\n+    h->value.len = r->header_end - r->header_start;\n+    h->value.data = r->header_start;\n+\n+    h->lowcase_key = h->key.data;\n+\n+    return NGX_OK;\n+}\n+\n+\n+void\n+ngx_http_spdy_request_headers_init(void)\n+{\n+    ngx_uint_t                       i;\n+    ngx_http_spdy_request_header_t  *h;\n+\n+    for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {\n+        h = &ngx_http_spdy_request_headers[i];\n+        h->hash = ngx_hash_key(h->header, h->len);\n+    }\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_method(ngx_http_request_t *r)\n+{\n+    size_t         k, len;\n+    ngx_uint_t     n;\n+    const u_char  *p, *m;\n+\n+    /*\n+     * This array takes less than 256 sequential bytes,\n+     * and if typical CPU cache line size is 64 bytes,\n+     * it is prefetched for 4 load operations.\n+     */\n+    static const struct {\n+        u_char            len;\n+        const u_char      method[11];\n+        uint32_t          value;\n+    } tests[] = {\n+        { 3, \"GET\",       NGX_HTTP_GET },\n+        { 4, \"POST\",      NGX_HTTP_POST },\n+        { 4, \"HEAD\",      NGX_HTTP_HEAD },\n+        { 7, \"OPTIONS\",   NGX_HTTP_OPTIONS },\n+        { 8, \"PROPFIND\",  NGX_HTTP_PROPFIND },\n+        { 3, \"PUT\",       NGX_HTTP_PUT },\n+        { 5, \"MKCOL\",     NGX_HTTP_MKCOL },\n+        { 6, \"DELETE\",    NGX_HTTP_DELETE },\n+        { 4, \"COPY\",      NGX_HTTP_COPY },\n+        { 4, \"MOVE\",      NGX_HTTP_MOVE },\n+        { 9, \"PROPPATCH\", NGX_HTTP_PROPPATCH },\n+        { 4, \"LOCK\",      NGX_HTTP_LOCK },\n+        { 6, \"UNLOCK\",    NGX_HTTP_UNLOCK },\n+        { 5, \"PATCH\",     NGX_HTTP_PATCH },\n+        { 5, \"TRACE\",     NGX_HTTP_TRACE }\n+    }, *test;\n+\n+    if (r->method_name.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :method header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    len = r->header_end - r->header_start;\n+\n+    r->method_name.len = len;\n+    r->method_name.data = r->header_start;\n+\n+    test = tests;\n+    n = sizeof(tests) / sizeof(tests[0]);\n+\n+    do {\n+        if (len == test->len) {\n+            p = r->method_name.data;\n+            m = test->method;\n+            k = len;\n+\n+            do {\n+                if (*p++ != *m++) {\n+                    goto next;\n+                }\n+            } while (--k);\n+\n+            r->method = test->value;\n+            return NGX_OK;\n+        }\n+\n+    next:\n+        test++;\n+\n+    } while (--n);\n+\n+    p = r->method_name.data;\n+\n+    do {\n+        if ((*p < 'A' || *p > 'Z') && *p != '_') {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid method: \\\"%V\\\"\",\n+                          &r->method_name);\n+\n+            return NGX_HTTP_PARSE_INVALID_HEADER;\n+        }\n+\n+        p++;\n+\n+    } while (--len);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_scheme(ngx_http_request_t *r)\n+{\n+    if (r->schema_start) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :schema header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    r->schema_start = r->header_start;\n+    r->schema_end = r->header_end;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_host(ngx_http_request_t *r)\n+{\n+    ngx_table_elt_t  *h;\n+\n+    if (r->headers_in.host) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :host header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    r->headers_in.host = h;\n+\n+    h->hash = r->header_hash;\n+\n+    h->key.len = r->header_name_end - r->header_name_start;\n+    h->key.data = r->header_name_start;\n+\n+    h->value.len = r->header_end - r->header_start;\n+    h->value.data = r->header_start;\n+\n+    h->lowcase_key = h->key.data;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_path(ngx_http_request_t *r)\n+{\n+    if (r->unparsed_uri.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :path header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    r->uri_start = r->header_start;\n+    r->uri_end = r->header_end;\n+\n+    if (ngx_http_parse_uri(r) != NGX_OK) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent invalid URI: \\\"%*s\\\"\",\n+                      r->uri_end - r->uri_start, r->uri_start);\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    if (ngx_http_process_request_uri(r) != NGX_OK) {\n+        /*\n+         * request has been finalized already\n+         * in ngx_http_process_request_uri()\n+         */\n+        return NGX_ABORT;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_parse_version(ngx_http_request_t *r)\n+{\n+    u_char  *p, ch;\n+\n+    if (r->http_protocol.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :version header\");\n+\n+        return NGX_HTTP_PARSE_INVALID_HEADER;\n+    }\n+\n+    p = r->header_start;\n+\n+    if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) {\n+        goto invalid;\n+    }\n+\n+    ch = *(p + 5);\n+\n+    if (ch < '1' || ch > '9') {\n+        goto invalid;\n+    }\n+\n+    r->http_major = ch - '0';\n+\n+    for (p += 6; p != r->header_end - 2; p++) {\n+\n+        ch = *p;\n+\n+        if (ch == '.') {\n+            break;\n+        }\n+\n+        if (ch < '0' || ch > '9') {\n+            goto invalid;\n+        }\n+\n+        r->http_major = r->http_major * 10 + ch - '0';\n+    }\n+\n+    if (*p != '.') {\n+        goto invalid;\n+    }\n+\n+    ch = *(p + 1);\n+\n+    if (ch < '0' || ch > '9') {\n+        goto invalid;\n+    }\n+\n+    r->http_minor = ch - '0';\n+\n+    for (p += 2; p != r->header_end; p++) {\n+\n+        ch = *p;\n+\n+        if (ch < '0' || ch > '9') {\n+            goto invalid;\n+        }\n+\n+        r->http_minor = r->http_minor * 10 + ch - '0';\n+    }\n+\n+    r->http_protocol.len = r->header_end - r->header_start;\n+    r->http_protocol.data = r->header_start;\n+    r->http_version = r->http_major * 1000 + r->http_minor;\n+\n+    return NGX_OK;\n+\n+invalid:\n+\n+    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                  \"client sent invalid http version: \\\"%*s\\\"\",\n+                  r->header_end - r->header_start, r->header_start);\n+\n+    return NGX_HTTP_PARSE_INVALID_HEADER;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_construct_request_line(ngx_http_request_t *r)\n+{\n+    u_char  *p;\n+\n+    if (r->method_name.len == 0\n+        || r->unparsed_uri.len == 0\n+        || r->http_protocol.len == 0)\n+    {\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.len = r->method_name.len + 1\n+                          + r->unparsed_uri.len + 1\n+                          + r->http_protocol.len;\n+\n+    p = ngx_pnalloc(r->pool, r->request_line.len + 1);\n+    if (p == NULL) {\n+        ngx_http_spdy_close_stream(r->spdy_stream,\n+                                   NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.data = p;\n+\n+    p = ngx_cpymem(p, r->method_name.data, r->method_name.len);\n+\n+    *p++ = ' ';\n+\n+    p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);\n+\n+    *p++ = ' ';\n+\n+    ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1);\n+\n+    /* some modules expect the space character after method name */\n+    r->method_name.data = r->request_line.data;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_spdy_run_request(ngx_http_request_t *r)\n+{\n+    ngx_uint_t                  i;\n+    ngx_list_part_t            *part;\n+    ngx_table_elt_t            *h;\n+    ngx_http_header_t          *hh;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    if (ngx_http_spdy_construct_request_line(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy http request line: \\\"%V\\\"\", &r->request_line);\n+\n+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+    part = &r->headers_in.headers.part;\n+    h = part->elts;\n+\n+    for (i = 0 ;; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            h = part->elts;\n+            i = 0;\n+        }\n+\n+        hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash,\n+                           h[i].lowcase_key, h[i].key.len);\n+\n+        if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) {\n+            return;\n+        }\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"spdy http header: \\\"%V: %V\\\"\", &h[i].key, &h[i].value);\n+    }\n+\n+    r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;\n+\n+    if (ngx_http_process_request_header(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    if (r->headers_in.content_length_n > 0 && r->spdy_stream->in_closed) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client prematurely closed stream\");\n+\n+        r->spdy_stream->skip_data = NGX_SPDY_DATA_ERROR;\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return;\n+    }\n+\n+    ngx_http_process_request(r);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_init_request_body(ngx_http_request_t *r)\n+{\n+    ngx_buf_t                 *buf;\n+    ngx_temp_file_t           *tf;\n+    ngx_http_request_body_t   *rb;\n+    ngx_http_core_loc_conf_t  *clcf;\n+\n+    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));\n+    if (rb == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_body = rb;\n+\n+    if (r->spdy_stream->in_closed) {\n+        return NGX_OK;\n+    }\n+\n+    rb->rest = r->headers_in.content_length_n;\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    if (r->request_body_in_file_only\n+        || rb->rest > (off_t) clcf->client_body_buffer_size\n+        || rb->rest < 0)\n+    {\n+        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));\n+        if (tf == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        tf->file.fd = NGX_INVALID_FILE;\n+        tf->file.log = r->connection->log;\n+        tf->path = clcf->client_body_temp_path;\n+        tf->pool = r->pool;\n+        tf->warn = \"a client request body is buffered to a temporary file\";\n+        tf->log_level = r->request_body_file_log_level;\n+        tf->persistent = r->request_body_in_persistent_file;\n+        tf->clean = r->request_body_in_clean_file;\n+\n+        if (r->request_body_file_group_access) {\n+            tf->access = 0660;\n+        }\n+\n+        rb->temp_file = tf;\n+\n+        if (r->spdy_stream->in_closed\n+            && ngx_create_temp_file(&tf->file, tf->path, tf->pool,\n+                                    tf->persistent, tf->clean, tf->access)\n+               != NGX_OK)\n+        {\n+            return NGX_ERROR;\n+        }\n+\n+        buf = ngx_calloc_buf(r->pool);\n+        if (buf == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+    } else {\n+\n+        if (rb->rest == 0) {\n+            return NGX_OK;\n+        }\n+\n+        buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest);\n+        if (buf == NULL) {\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    rb->buf = buf;\n+\n+    rb->bufs = ngx_alloc_chain_link(r->pool);\n+    if (rb->bufs == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    rb->bufs->buf = buf;\n+    rb->bufs->next = NULL;\n+\n+    rb->rest = 0;\n+\n+    return NGX_OK;\n+}\n+\n+\n+ngx_int_t\n+ngx_http_spdy_read_request_body(ngx_http_request_t *r,\n+    ngx_http_client_body_handler_pt post_handler)\n+{\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy read request body\");\n+\n+    stream = r->spdy_stream;\n+\n+    switch (stream->skip_data) {\n+\n+    case NGX_SPDY_DATA_DISCARD:\n+        post_handler(r);\n+        return NGX_OK;\n+\n+    case NGX_SPDY_DATA_ERROR:\n+        if (r->headers_in.content_length_n == -1) {\n+            return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;\n+        } else {\n+            return NGX_HTTP_BAD_REQUEST;\n+        }\n+\n+    case NGX_SPDY_DATA_INTERNAL_ERROR:\n+        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n+    }\n+\n+    if (!r->request_body->buf && ngx_http_spdy_init_request_body(r) != NGX_OK) {\n+        stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;\n+        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n+    }\n+\n+    if (stream->in_closed) {\n+        post_handler(r);\n+        return NGX_OK;\n+    }\n+\n+    r->request_body->post_handler = post_handler;\n+\n+    r->read_event_handler = ngx_http_test_reading;\n+    r->write_event_handler = ngx_http_request_empty_handler;\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream, ngx_uint_t status)\n+{\n+    ngx_event_t       *rev;\n+    ngx_connection_t  *fc;\n+\n+    if (ngx_http_spdy_send_rst_stream(sc, stream->id, status,\n+                                      NGX_SPDY_HIGHEST_PRIORITY)\n+        == NGX_ERROR)\n+    {\n+        return NGX_ERROR;\n+    }\n+\n+    stream->out_closed = 1;\n+\n+    fc = stream->request->connection;\n+    fc->error = 1;\n+\n+    rev = fc->read;\n+    rev->handler(rev);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_spdy_close_stream_handler(ngx_event_t *ev)\n+{\n+    ngx_connection_t    *fc;\n+    ngx_http_request_t  *r;\n+\n+    fc = ev->data;\n+    r = fc->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy close stream handler\");\n+\n+    ngx_http_spdy_close_stream(r->spdy_stream, 0);\n+}\n+\n+\n+void\n+ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc)\n+{\n+    ngx_event_t                  *ev;\n+    ngx_connection_t             *fc;\n+    ngx_http_spdy_stream_t      **index, *s;\n+    ngx_http_spdy_srv_conf_t     *sscf;\n+    ngx_http_spdy_connection_t   *sc;\n+\n+    sc = stream->connection;\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy close stream %ui, queued %ui, processing %ui\",\n+                   stream->id, stream->queued, sc->processing);\n+\n+    fc = stream->request->connection;\n+\n+    if (stream->queued) {\n+        fc->write->handler = ngx_http_spdy_close_stream_handler;\n+        return;\n+    }\n+\n+    if (!stream->out_closed) {\n+        if (ngx_http_spdy_send_rst_stream(sc, stream->id,\n+                                          NGX_SPDY_INTERNAL_ERROR,\n+                                          stream->priority)\n+            != NGX_OK)\n+        {\n+            sc->connection->error = 1;\n+        }\n+    }\n+\n+    if (sc->stream == stream) {\n+        sc->stream = NULL;\n+    }\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id);\n+\n+    for ( ;; ) {\n+        s = *index;\n+\n+        if (s == NULL) {\n+            break;\n+        }\n+\n+        if (s == stream) {\n+            *index = s->index;\n+            break;\n+        }\n+\n+        index = &s->index;\n+    }\n+\n+    ngx_http_free_request(stream->request, rc);\n+\n+    ev = fc->read;\n+\n+    if (ev->active || ev->disabled) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,\n+                      \"fake read event was activated\");\n+    }\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    ev = fc->write;\n+\n+    if (ev->active || ev->disabled) {\n+        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,\n+                      \"fake write event was activated\");\n+    }\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    fc->data = sc->free_fake_connections;\n+    sc->free_fake_connections = fc;\n+\n+    sc->processing--;\n+\n+    if (sc->processing || sc->blocked) {\n+        return;\n+    }\n+\n+    ev = sc->connection->read;\n+\n+    ev->handler = ngx_http_spdy_handle_connection_handler;\n+    ngx_post_event(ev, &ngx_posted_events);\n+}\n+\n+\n+static void\n+ngx_http_spdy_handle_connection_handler(ngx_event_t *rev)\n+{\n+    ngx_connection_t  *c;\n+\n+    rev->handler = ngx_http_spdy_read_handler;\n+\n+    if (rev->ready) {\n+        ngx_http_spdy_read_handler(rev);\n+        return;\n+    }\n+\n+    c = rev->data;\n+\n+    ngx_http_spdy_handle_connection(c->data);\n+}\n+\n+\n+static void\n+ngx_http_spdy_keepalive_handler(ngx_event_t *rev)\n+{\n+    ngx_connection_t            *c;\n+    ngx_http_spdy_srv_conf_t    *sscf;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    c = rev->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"spdy keepalive handler\");\n+\n+    if (rev->timedout || c->close) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+#if (NGX_HAVE_KQUEUE)\n+\n+    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n+        if (rev->pending_eof) {\n+            c->log->handler = NULL;\n+            ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,\n+                          \"kevent() reported that client %V closed \"\n+                          \"keepalive connection\", &c->addr_text);\n+#if (NGX_HTTP_SSL)\n+            if (c->ssl) {\n+                c->ssl->no_send_shutdown = 1;\n+            }\n+#endif\n+            ngx_http_close_connection(c);\n+            return;\n+        }\n+    }\n+\n+#endif\n+\n+    c->destroyed = 0;\n+    c->idle = 0;\n+    ngx_reusable_connection(c, 0);\n+\n+    sc = c->data;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);\n+    if (sc->pool == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    sc->streams_index = ngx_pcalloc(sc->pool,\n+                                    ngx_http_spdy_streams_index_size(sscf)\n+                                    * sizeof(ngx_http_spdy_stream_t *));\n+    if (sc->streams_index == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    c->write->handler = ngx_http_spdy_write_handler;\n+\n+    rev->handler = ngx_http_spdy_read_handler;\n+    ngx_http_spdy_read_handler(rev);\n+}\n+\n+\n+static void\n+ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,\n+    ngx_int_t rc)\n+{\n+    ngx_uint_t                 i, size;\n+    ngx_event_t               *ev;\n+    ngx_connection_t          *c, *fc;\n+    ngx_http_request_t        *r;\n+    ngx_http_spdy_stream_t    *stream;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    c = sc->connection;\n+\n+    if (!sc->processing) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    c->error = 1;\n+    c->read->handler = ngx_http_empty_handler;\n+    c->write->handler = ngx_http_empty_handler;\n+\n+    sc->last_out = NULL;\n+\n+    sc->blocked = 1;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    size = ngx_http_spdy_streams_index_size(sscf);\n+\n+    for (i = 0; i < size; i++) {\n+        stream = sc->streams_index[i];\n+\n+        while (stream) {\n+            stream->handled = 0;\n+\n+            r = stream->request;\n+            fc = r->connection;\n+\n+            fc->error = 1;\n+\n+            if (stream->queued) {\n+                stream->queued = 0;\n+\n+                ev = fc->write;\n+                ev->delayed = 0;\n+\n+            } else {\n+                ev = fc->read;\n+            }\n+\n+            stream = stream->index;\n+\n+            ev->eof = 1;\n+            ev->handler(ev);\n+        }\n+    }\n+\n+    sc->blocked = 0;\n+\n+    if (sc->processing) {\n+        return;\n+    }\n+\n+    ngx_http_close_connection(c);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc, ssize_t delta)\n+{\n+    ngx_uint_t                 i, size;\n+    ngx_event_t               *wev;\n+    ngx_http_spdy_stream_t    *stream, *sn;\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,\n+                                        ngx_http_spdy_module);\n+\n+    size = ngx_http_spdy_streams_index_size(sscf);\n+\n+    for (i = 0; i < size; i++) {\n+\n+        for (stream = sc->streams_index[i]; stream; stream = sn) {\n+            sn = stream->index;\n+\n+            if (delta > 0\n+                && stream->send_window\n+                      > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta))\n+            {\n+                if (ngx_http_spdy_terminate_stream(sc, stream,\n+                                                   NGX_SPDY_FLOW_CONTROL_ERROR)\n+                    == NGX_ERROR)\n+                {\n+                    return NGX_ERROR;\n+                }\n+\n+                continue;\n+            }\n+\n+            stream->send_window += delta;\n+\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"spdy:%ui adjust window:%z\",\n+                           stream->id, stream->send_window);\n+\n+            if (stream->send_window > 0 && stream->exhausted) {\n+                stream->exhausted = 0;\n+\n+                wev = stream->request->connection->write;\n+\n+                if (!wev->timer_set) {\n+                    wev->delayed = 0;\n+                    wev->handler(wev);\n+                }\n+            }\n+        }\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_spdy_pool_cleanup(void *data)\n+{\n+    ngx_http_spdy_connection_t  *sc = data;\n+\n+    if (sc->pool) {\n+        ngx_destroy_pool(sc->pool);\n+    }\n+}\n+\n+\n+static void *\n+ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size)\n+{\n+    ngx_http_spdy_connection_t *sc = opaque;\n+\n+    return ngx_palloc(sc->connection->pool, items * size);\n+}\n+\n+\n+static void\n+ngx_http_spdy_zfree(void *opaque, void *address)\n+{\n+#if 0\n+    ngx_http_spdy_connection_t *sc = opaque;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy zfree: %p\", address);\n+#endif\n+}\ndiff -uNr a/src/http/ngx_http_spdy_filter_module.c b/src/http/ngx_http_spdy_filter_module.c\n--- a/src/http/ngx_http_spdy_filter_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy_filter_module.c\t2020-01-03 11:57:43.000000000 +0800\n@@ -0,0 +1,1222 @@\n+\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <nginx.h>\n+#include <ngx_http_spdy_module.h>\n+\n+#include <zlib.h>\n+\n+\n+#define ngx_http_spdy_nv_nsize(h)  (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1)\n+#define ngx_http_spdy_nv_vsize(h)  (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1)\n+\n+#define ngx_http_spdy_nv_write_num   ngx_spdy_frame_write_uint32\n+#define ngx_http_spdy_nv_write_nlen  ngx_spdy_frame_write_uint32\n+#define ngx_http_spdy_nv_write_vlen  ngx_spdy_frame_write_uint32\n+\n+#define ngx_http_spdy_nv_write_name(p, h)                                     \\\n+    ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1)\n+\n+#define ngx_http_spdy_nv_write_val(p, h)                                      \\\n+    ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1)\n+\n+\n+static ngx_chain_t *ngx_http_spdy_send_chain(ngx_connection_t *fc,\n+    ngx_chain_t *in, off_t limit);\n+\n+static ngx_inline ngx_int_t ngx_http_spdy_filter_send(\n+    ngx_connection_t *fc, ngx_http_spdy_stream_t *stream);\n+static ngx_inline ngx_int_t ngx_http_spdy_flow_control(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);\n+static void ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream);\n+\n+static ngx_chain_t *ngx_http_spdy_filter_get_shadow(\n+    ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);\n+static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame(\n+    ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first,\n+    ngx_chain_t *last);\n+\n+static ngx_int_t ngx_http_spdy_syn_frame_handler(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);\n+static ngx_int_t ngx_http_spdy_data_frame_handler(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);\n+static ngx_inline void ngx_http_spdy_handle_frame(\n+    ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame);\n+static ngx_inline void ngx_http_spdy_handle_stream(\n+    ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);\n+\n+static void ngx_http_spdy_filter_cleanup(void *data);\n+\n+static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf);\n+\n+\n+static ngx_http_module_t  ngx_http_spdy_filter_module_ctx = {\n+    NULL,                                  /* preconfiguration */\n+    ngx_http_spdy_filter_init,             /* postconfiguration */\n+\n+    NULL,                                  /* create main configuration */\n+    NULL,                                  /* init main configuration */\n+\n+    NULL,                                  /* create server configuration */\n+    NULL,                                  /* merge server configuration */\n+\n+    NULL,                                  /* create location configuration */\n+    NULL                                   /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_spdy_filter_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_spdy_filter_module_ctx,      /* module context */\n+    NULL,                                  /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_header_filter(ngx_http_request_t *r)\n+{\n+    int                           rc;\n+    size_t                        len;\n+    u_char                       *p, *buf, *last;\n+    ngx_buf_t                    *b;\n+    ngx_str_t                     host;\n+    ngx_uint_t                    i, j, count, port;\n+    ngx_chain_t                  *cl;\n+    ngx_list_part_t              *part, *pt;\n+    ngx_table_elt_t              *header, *h;\n+    ngx_connection_t             *c;\n+    ngx_http_cleanup_t           *cln;\n+    ngx_http_core_loc_conf_t     *clcf;\n+    ngx_http_core_srv_conf_t     *cscf;\n+    ngx_http_spdy_stream_t       *stream;\n+    ngx_http_spdy_out_frame_t    *frame;\n+    ngx_http_spdy_connection_t   *sc;\n+    struct sockaddr_in           *sin;\n+#if (NGX_HAVE_INET6)\n+    struct sockaddr_in6          *sin6;\n+#endif\n+    u_char                        addr[NGX_SOCKADDR_STRLEN];\n+\n+    if (!r->spdy_stream) {\n+        return ngx_http_next_header_filter(r);\n+    }\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"spdy header filter\");\n+\n+    if (r->header_sent) {\n+        return NGX_OK;\n+    }\n+\n+    r->header_sent = 1;\n+\n+    if (r != r->main) {\n+        return NGX_OK;\n+    }\n+\n+    c = r->connection;\n+\n+    if (r->method == NGX_HTTP_HEAD) {\n+        r->header_only = 1;\n+    }\n+\n+    switch (r->headers_out.status) {\n+\n+    case NGX_HTTP_OK:\n+    case NGX_HTTP_PARTIAL_CONTENT:\n+        break;\n+\n+    case NGX_HTTP_NOT_MODIFIED:\n+        r->header_only = 1;\n+        break;\n+\n+    case NGX_HTTP_NO_CONTENT:\n+        r->header_only = 1;\n+\n+        ngx_str_null(&r->headers_out.content_type);\n+\n+        r->headers_out.content_length = NULL;\n+        r->headers_out.content_length_n = -1;\n+\n+        /* fall through */\n+\n+    default:\n+        r->headers_out.last_modified_time = -1;\n+        r->headers_out.last_modified = NULL;\n+    }\n+\n+    len = NGX_SPDY_NV_NUM_SIZE\n+          + ngx_http_spdy_nv_nsize(\":version\")\n+          + ngx_http_spdy_nv_vsize(\"HTTP/1.1\")\n+          + ngx_http_spdy_nv_nsize(\":status\")\n+          + (r->headers_out.status_line.len\n+             ? NGX_SPDY_NV_VLEN_SIZE + r->headers_out.status_line.len\n+             : ngx_http_spdy_nv_vsize(\"418\"));\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    if (r->headers_out.server == NULL) {\n+        len += ngx_http_spdy_nv_nsize(\"server\");\n+        len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER)\n+                                   : ngx_http_spdy_nv_vsize(\"nginx\");\n+    }\n+\n+    if (r->headers_out.date == NULL) {\n+        len += ngx_http_spdy_nv_nsize(\"date\")\n+               + ngx_http_spdy_nv_vsize(\"Wed, 31 Dec 1986 10:00:00 GMT\");\n+    }\n+\n+    if (r->headers_out.content_type.len) {\n+        len += ngx_http_spdy_nv_nsize(\"content-type\")\n+               + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len;\n+\n+        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n+            && r->headers_out.charset.len)\n+        {\n+            len += sizeof(\"; charset=\") - 1 + r->headers_out.charset.len;\n+        }\n+    }\n+\n+    if (r->headers_out.content_length == NULL\n+        && r->headers_out.content_length_n >= 0)\n+    {\n+        len += ngx_http_spdy_nv_nsize(\"content-length\")\n+               + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN;\n+    }\n+\n+    if (r->headers_out.last_modified == NULL\n+        && r->headers_out.last_modified_time != -1)\n+    {\n+        len += ngx_http_spdy_nv_nsize(\"last-modified\")\n+               + ngx_http_spdy_nv_vsize(\"Wed, 31 Dec 1986 10:00:00 GMT\");\n+    }\n+\n+    if (r->headers_out.location\n+        && r->headers_out.location->value.len\n+        && r->headers_out.location->value.data[0] == '/')\n+    {\n+        r->headers_out.location->hash = 0;\n+\n+        if (clcf->server_name_in_redirect) {\n+            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+            host = cscf->server_name;\n+\n+        } else if (r->headers_in.server.len) {\n+            host = r->headers_in.server;\n+\n+        } else {\n+            host.len = NGX_SOCKADDR_STRLEN;\n+            host.data = addr;\n+\n+            if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {\n+                return NGX_ERROR;\n+            }\n+        }\n+\n+        switch (c->local_sockaddr->sa_family) {\n+\n+#if (NGX_HAVE_INET6)\n+        case AF_INET6:\n+            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;\n+            port = ntohs(sin6->sin6_port);\n+            break;\n+#endif\n+#if (NGX_HAVE_UNIX_DOMAIN)\n+        case AF_UNIX:\n+            port = 0;\n+            break;\n+#endif\n+        default: /* AF_INET */\n+            sin = (struct sockaddr_in *) c->local_sockaddr;\n+            port = ntohs(sin->sin_port);\n+            break;\n+        }\n+\n+        len += ngx_http_spdy_nv_nsize(\"location\")\n+               + ngx_http_spdy_nv_vsize(\"https://\")\n+               + host.len\n+               + r->headers_out.location->value.len;\n+\n+        if (clcf->port_in_redirect) {\n+\n+#if (NGX_HTTP_SSL)\n+            if (c->ssl)\n+                port = (port == 443) ? 0 : port;\n+            else\n+#endif\n+                port = (port == 80) ? 0 : port;\n+\n+        } else {\n+            port = 0;\n+        }\n+\n+        if (port) {\n+            len += sizeof(\":65535\") - 1;\n+        }\n+\n+    } else {\n+        ngx_str_null(&host);\n+        port = 0;\n+    }\n+\n+    part = &r->headers_out.headers.part;\n+    header = part->elts;\n+\n+    for (i = 0; /* void */; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            header = part->elts;\n+            i = 0;\n+        }\n+\n+        if (header[i].hash == 0) {\n+            continue;\n+        }\n+\n+        len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len\n+               + NGX_SPDY_NV_VLEN_SIZE  + header[i].value.len;\n+    }\n+\n+    buf = ngx_alloc(len, r->pool->log);\n+    if (buf == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    last = buf + NGX_SPDY_NV_NUM_SIZE;\n+\n+    last = ngx_http_spdy_nv_write_name(last, \":version\");\n+    last = ngx_http_spdy_nv_write_val(last, \"HTTP/1.1\");\n+\n+    last = ngx_http_spdy_nv_write_name(last, \":status\");\n+\n+    if (r->headers_out.status_line.len) {\n+        last = ngx_http_spdy_nv_write_vlen(last,\n+                                           r->headers_out.status_line.len);\n+        last = ngx_cpymem(last, r->headers_out.status_line.data,\n+                          r->headers_out.status_line.len);\n+    } else {\n+        last = ngx_http_spdy_nv_write_vlen(last, 3);\n+        last = ngx_sprintf(last, \"%03ui\", r->headers_out.status);\n+    }\n+\n+    count = 2;\n+\n+    if (r->headers_out.server == NULL) {\n+        last = ngx_http_spdy_nv_write_name(last, \"server\");\n+        last = clcf->server_tokens\n+               ? ngx_http_spdy_nv_write_val(last, NGINX_VER)\n+               : ngx_http_spdy_nv_write_val(last, \"nginx\");\n+\n+        count++;\n+    }\n+\n+    if (r->headers_out.date == NULL) {\n+        last = ngx_http_spdy_nv_write_name(last, \"date\");\n+\n+        last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len);\n+\n+        last = ngx_cpymem(last, ngx_cached_http_time.data,\n+                          ngx_cached_http_time.len);\n+\n+        count++;\n+    }\n+\n+    if (r->headers_out.content_type.len) {\n+\n+        last = ngx_http_spdy_nv_write_name(last, \"content-type\");\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_cpymem(p, r->headers_out.content_type.data,\n+                          r->headers_out.content_type.len);\n+\n+        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n+            && r->headers_out.charset.len)\n+        {\n+            last = ngx_cpymem(last, \"; charset=\", sizeof(\"; charset=\") - 1);\n+\n+            last = ngx_cpymem(last, r->headers_out.charset.data,\n+                              r->headers_out.charset.len);\n+\n+            /* update r->headers_out.content_type for possible logging */\n+\n+            r->headers_out.content_type.len = last - p;\n+            r->headers_out.content_type.data = p;\n+        }\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           r->headers_out.content_type.len);\n+\n+        count++;\n+    }\n+\n+    if (r->headers_out.content_length == NULL\n+        && r->headers_out.content_length_n >= 0)\n+    {\n+        last = ngx_http_spdy_nv_write_name(last, \"content-length\");\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_sprintf(p, \"%O\", r->headers_out.content_length_n);\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           last - p);\n+\n+        count++;\n+    }\n+\n+    if (r->headers_out.last_modified == NULL\n+        && r->headers_out.last_modified_time != -1)\n+    {\n+        last = ngx_http_spdy_nv_write_name(last, \"last-modified\");\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_http_time(p, r->headers_out.last_modified_time);\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           last - p);\n+\n+        count++;\n+    }\n+\n+    if (host.data) {\n+\n+        last = ngx_http_spdy_nv_write_name(last, \"location\");\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_cpymem(p, \"http\", sizeof(\"http\") - 1);\n+\n+#if (NGX_HTTP_SSL)\n+        if (c->ssl) {\n+            *last++ ='s';\n+        }\n+#endif\n+\n+        *last++ = ':'; *last++ = '/'; *last++ = '/';\n+\n+        last = ngx_cpymem(last, host.data, host.len);\n+\n+        if (port) {\n+            last = ngx_sprintf(last, \":%ui\", port);\n+        }\n+\n+        last = ngx_cpymem(last, r->headers_out.location->value.data,\n+                          r->headers_out.location->value.len);\n+\n+        /* update r->headers_out.location->value for possible logging */\n+\n+        r->headers_out.location->value.len = last - p;\n+        r->headers_out.location->value.data = p;\n+        ngx_str_set(&r->headers_out.location->key, \"location\");\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           r->headers_out.location->value.len);\n+\n+        count++;\n+    }\n+\n+    part = &r->headers_out.headers.part;\n+    header = part->elts;\n+\n+    for (i = 0; /* void */; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            header = part->elts;\n+            i = 0;\n+        }\n+\n+        if (header[i].hash == 0 || header[i].hash == 2) {\n+            continue;\n+        }\n+\n+        last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len);\n+\n+        ngx_strlow(last, header[i].key.data, header[i].key.len);\n+        last += header[i].key.len;\n+\n+        p = last + NGX_SPDY_NV_VLEN_SIZE;\n+\n+        last = ngx_cpymem(p, header[i].value.data, header[i].value.len);\n+\n+        pt = part;\n+        h = header;\n+\n+        for (j = i + 1; /* void */; j++) {\n+\n+            if (j >= pt->nelts) {\n+                if (pt->next == NULL) {\n+                    break;\n+                }\n+\n+                pt = pt->next;\n+                h = pt->elts;\n+                j = 0;\n+            }\n+\n+            if (h[j].hash == 0 || h[j].hash == 2\n+                || h[j].key.len != header[i].key.len\n+                || ngx_strncasecmp(header[i].key.data, h[j].key.data,\n+                                   header[i].key.len))\n+            {\n+                continue;\n+            }\n+\n+            if (h[j].value.len) {\n+                if (last != p) {\n+                    *last++ = '\\0';\n+                }\n+\n+                last = ngx_cpymem(last, h[j].value.data, h[j].value.len);\n+            }\n+\n+            h[j].hash = 2;\n+        }\n+\n+        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,\n+                                           last - p);\n+\n+        count++;\n+    }\n+\n+    (void) ngx_http_spdy_nv_write_num(buf, count);\n+\n+    stream = r->spdy_stream;\n+    sc = stream->connection;\n+\n+    len = last - buf;\n+\n+    b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE\n+                                     + NGX_SPDY_SYN_REPLY_SIZE\n+                                     + deflateBound(&sc->zstream_out, len));\n+    if (b == NULL) {\n+        ngx_free(buf);\n+        return NGX_ERROR;\n+    }\n+\n+    b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE;\n+\n+    sc->zstream_out.next_in = buf;\n+    sc->zstream_out.avail_in = len;\n+    sc->zstream_out.next_out = b->last;\n+    sc->zstream_out.avail_out = b->end - b->last;\n+\n+    rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH);\n+\n+    ngx_free(buf);\n+\n+    if (rc != Z_OK) {\n+        ngx_log_error(NGX_LOG_ALERT, c->log, 0, \"deflate() failed: %d\", rc);\n+        return NGX_ERROR;\n+    }\n+\n+    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                   \"spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n+                   sc->zstream_out.next_in, sc->zstream_out.next_out,\n+                   sc->zstream_out.avail_in, sc->zstream_out.avail_out,\n+                   rc);\n+\n+    b->last = sc->zstream_out.next_out;\n+\n+    p = b->pos;\n+    p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY);\n+\n+    len = b->last - b->pos;\n+\n+    r->header_size = len;\n+\n+    len -= NGX_SPDY_FRAME_HEADER_SIZE;\n+\n+    if (r->header_only) {\n+        b->last_buf = 1;\n+        p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, len);\n+\n+    } else {\n+        p = ngx_spdy_frame_write_flags_and_len(p, 0, len);\n+    }\n+\n+    (void) ngx_spdy_frame_write_sid(p, stream->id);\n+\n+    cl = ngx_alloc_chain_link(r->pool);\n+    if (cl == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    cl->buf = b;\n+    cl->next = NULL;\n+\n+    frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t));\n+    if (frame == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    frame->first = cl;\n+    frame->last = cl;\n+    frame->handler = ngx_http_spdy_syn_frame_handler;\n+    frame->stream = stream;\n+    frame->length = len;\n+    frame->priority = stream->priority;\n+    frame->blocked = 1;\n+    frame->fin = r->header_only;\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,\n+                   \"spdy:%ui create SYN_REPLY frame %p: len:%uz\",\n+                   stream->id, frame, frame->length);\n+\n+    ngx_http_spdy_queue_blocked_frame(sc, frame);\n+\n+    cln = ngx_http_cleanup_add(r, 0);\n+    if (cln == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    cln->handler = ngx_http_spdy_filter_cleanup;\n+    cln->data = stream;\n+\n+    stream->queued = 1;\n+\n+    c->send_chain = ngx_http_spdy_send_chain;\n+    c->need_last_buf = 1;\n+\n+    return ngx_http_spdy_filter_send(c, stream);\n+}\n+\n+\n+static ngx_chain_t *\n+ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)\n+{\n+    off_t                        size, offset;\n+    size_t                       rest, frame_size;\n+    ngx_chain_t                 *cl, *out, **ln;\n+    ngx_http_request_t          *r;\n+    ngx_http_spdy_stream_t      *stream;\n+    ngx_http_spdy_loc_conf_t    *slcf;\n+    ngx_http_spdy_out_frame_t   *frame;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    r = fc->data;\n+    stream = r->spdy_stream;\n+\n+#if (NGX_SUPPRESS_WARN)\n+    size = 0;\n+#endif\n+\n+    while (in) {\n+        size = ngx_buf_size(in->buf);\n+\n+        if (size || in->buf->last_buf) {\n+            break;\n+        }\n+\n+        in = in->next;\n+    }\n+\n+    if (in == NULL) {\n+\n+        if (stream->queued) {\n+            fc->write->delayed = 1;\n+        } else {\n+            fc->buffered &= ~NGX_SPDY_BUFFERED;\n+        }\n+\n+        return NULL;\n+    }\n+\n+    sc = stream->connection;\n+\n+    if (size && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {\n+        fc->write->delayed = 1;\n+        return in;\n+    }\n+\n+    if (limit == 0 || limit > (off_t) sc->send_window) {\n+        limit = sc->send_window;\n+    }\n+\n+    if (limit > stream->send_window) {\n+        limit = (stream->send_window > 0) ? stream->send_window : 0;\n+    }\n+\n+    if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {\n+        cl = ngx_alloc_chain_link(r->pool);\n+        if (cl == NULL) {\n+            return NGX_CHAIN_ERROR;\n+        }\n+\n+        cl->buf = in->buf;\n+        in->buf = cl->buf->shadow;\n+\n+        offset = ngx_buf_in_memory(in->buf)\n+                 ? (cl->buf->pos - in->buf->pos)\n+                 : (cl->buf->file_pos - in->buf->file_pos);\n+\n+        cl->next = stream->free_bufs;\n+        stream->free_bufs = cl;\n+\n+    } else {\n+        offset = 0;\n+    }\n+\n+#if (NGX_SUPPRESS_WARN)\n+    cl = NULL;\n+#endif\n+\n+    slcf = ngx_http_get_module_loc_conf(r, ngx_http_spdy_module);\n+\n+    frame_size = (limit <= (off_t) slcf->chunk_size) ? (size_t) limit\n+                                                     : slcf->chunk_size;\n+\n+    for ( ;; ) {\n+        ln = &out;\n+        rest = frame_size;\n+\n+        while ((off_t) rest >= size) {\n+\n+            if (offset) {\n+                cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,\n+                                                     offset, size);\n+                if (cl == NULL) {\n+                    return NGX_CHAIN_ERROR;\n+                }\n+\n+                offset = 0;\n+\n+            } else {\n+                cl = ngx_alloc_chain_link(r->pool);\n+                if (cl == NULL) {\n+                    return NGX_CHAIN_ERROR;\n+                }\n+\n+                cl->buf = in->buf;\n+            }\n+\n+            *ln = cl;\n+            ln = &cl->next;\n+\n+            rest -= (size_t) size;\n+            in = in->next;\n+\n+            if (in == NULL) {\n+                frame_size -= rest;\n+                rest = 0;\n+                break;\n+            }\n+\n+            size = ngx_buf_size(in->buf);\n+        }\n+\n+        if (rest) {\n+            cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,\n+                                                 offset, rest);\n+            if (cl == NULL) {\n+                return NGX_CHAIN_ERROR;\n+            }\n+\n+            cl->buf->flush = 0;\n+            cl->buf->last_buf = 0;\n+\n+            *ln = cl;\n+\n+            offset += rest;\n+            size -= rest;\n+        }\n+\n+        frame = ngx_http_spdy_filter_get_data_frame(stream, frame_size,\n+                                                    out, cl);\n+        if (frame == NULL) {\n+            return NGX_CHAIN_ERROR;\n+        }\n+\n+        ngx_http_spdy_queue_frame(sc, frame);\n+\n+        sc->send_window -= frame_size;\n+\n+        stream->send_window -= frame_size;\n+        stream->queued++;\n+\n+        if (in == NULL) {\n+            break;\n+        }\n+\n+        limit -= frame_size;\n+\n+        if (limit == 0) {\n+            break;\n+        }\n+\n+        if (limit < (off_t) slcf->chunk_size) {\n+            frame_size = (size_t) limit;\n+        }\n+    }\n+\n+    if (offset) {\n+        cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size);\n+        if (cl == NULL) {\n+            return NGX_CHAIN_ERROR;\n+        }\n+\n+        in->buf = cl->buf;\n+        ngx_free_chain(r->pool, cl);\n+    }\n+\n+    if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) {\n+        return NGX_CHAIN_ERROR;\n+    }\n+\n+    if (in && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {\n+        fc->write->delayed = 1;\n+    }\n+\n+    return in;\n+}\n+\n+\n+static ngx_chain_t *\n+ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf,\n+    off_t offset, off_t size)\n+{\n+    ngx_buf_t    *chunk;\n+    ngx_chain_t  *cl;\n+\n+    cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);\n+    if (cl == NULL) {\n+        return NULL;\n+    }\n+\n+    chunk = cl->buf;\n+\n+    ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));\n+\n+    chunk->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow;\n+    chunk->shadow = buf;\n+\n+    if (ngx_buf_in_memory(chunk)) {\n+        chunk->pos += offset;\n+        chunk->last = chunk->pos + size;\n+    }\n+\n+    if (chunk->in_file) {\n+        chunk->file_pos += offset;\n+        chunk->file_last = chunk->file_pos + size;\n+    }\n+\n+    return cl;\n+}\n+\n+\n+static ngx_http_spdy_out_frame_t *\n+ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream,\n+    size_t len, ngx_chain_t *first, ngx_chain_t *last)\n+{\n+    u_char                     *p;\n+    ngx_buf_t                  *buf;\n+    ngx_uint_t                  flags;\n+    ngx_chain_t                *cl;\n+    ngx_http_spdy_out_frame_t  *frame;\n+\n+\n+    frame = stream->free_frames;\n+\n+    if (frame) {\n+        stream->free_frames = frame->next;\n+\n+    } else {\n+        frame = ngx_palloc(stream->request->pool,\n+                           sizeof(ngx_http_spdy_out_frame_t));\n+        if (frame == NULL) {\n+            return NULL;\n+        }\n+    }\n+\n+    flags = last->buf->last_buf ? NGX_SPDY_FLAG_FIN : 0;\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,\n+                   \"spdy:%ui create DATA frame %p: len:%uz flags:%ui\",\n+                   stream->id, frame, len, flags);\n+\n+    cl = ngx_chain_get_free_buf(stream->request->pool,\n+                                &stream->free_data_headers);\n+    if (cl == NULL) {\n+        return NULL;\n+    }\n+\n+    buf = cl->buf;\n+\n+    if (buf->start) {\n+        p = buf->start;\n+        buf->pos = p;\n+\n+        p += NGX_SPDY_SID_SIZE;\n+\n+        (void) ngx_spdy_frame_write_flags_and_len(p, flags, len);\n+\n+    } else {\n+        p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE);\n+        if (p == NULL) {\n+            return NULL;\n+        }\n+\n+        buf->pos = p;\n+        buf->start = p;\n+\n+        p = ngx_spdy_frame_write_sid(p, stream->id);\n+        p = ngx_spdy_frame_write_flags_and_len(p, flags, len);\n+\n+        buf->last = p;\n+        buf->end = p;\n+\n+        buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame;\n+        buf->memory = 1;\n+    }\n+\n+    cl->next = first;\n+    first = cl;\n+\n+    last->buf->flush = 1;\n+\n+    frame->first = first;\n+    frame->last = last;\n+    frame->handler = ngx_http_spdy_data_frame_handler;\n+    frame->stream = stream;\n+    frame->length = len;\n+    frame->priority = stream->priority;\n+    frame->blocked = 0;\n+    frame->fin = last->buf->last_buf;\n+\n+    return frame;\n+}\n+\n+\n+static ngx_inline ngx_int_t\n+ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream)\n+{\n+    stream->blocked = 1;\n+\n+    if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) {\n+        fc->error = 1;\n+        return NGX_ERROR;\n+    }\n+\n+    stream->blocked = 0;\n+\n+    if (stream->queued) {\n+        fc->buffered |= NGX_SPDY_BUFFERED;\n+        fc->write->delayed = 1;\n+        return NGX_AGAIN;\n+    }\n+\n+    fc->buffered &= ~NGX_SPDY_BUFFERED;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_inline ngx_int_t\n+ngx_http_spdy_flow_control(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream)\n+{\n+    if (stream->send_window <= 0) {\n+        stream->exhausted = 1;\n+        return NGX_DECLINED;\n+    }\n+\n+    if (sc->send_window == 0) {\n+        ngx_http_spdy_waiting_queue(sc, stream);\n+        return NGX_DECLINED;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream)\n+{\n+    ngx_queue_t             *q;\n+    ngx_http_spdy_stream_t  *s;\n+\n+    if (stream->handled) {\n+        return;\n+    }\n+\n+    stream->handled = 1;\n+\n+    for (q = ngx_queue_last(&sc->waiting);\n+         q != ngx_queue_sentinel(&sc->waiting);\n+         q = ngx_queue_prev(q))\n+    {\n+        s = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);\n+\n+        /*\n+         * NB: higher values represent lower priorities.\n+         */\n+        if (stream->priority >= s->priority) {\n+            break;\n+        }\n+    }\n+\n+    ngx_queue_insert_after(q, &stream->queue);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_buf_t               *buf;\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    buf = frame->first->buf;\n+\n+    if (buf->pos != buf->last) {\n+        return NGX_AGAIN;\n+    }\n+\n+    stream = frame->stream;\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy:%ui SYN_REPLY frame %p was sent\", stream->id, frame);\n+\n+    ngx_free_chain(stream->request->pool, frame->first);\n+\n+    ngx_http_spdy_handle_frame(stream, frame);\n+\n+    ngx_http_spdy_handle_stream(sc, stream);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_buf_t               *buf;\n+    ngx_chain_t             *cl, *ln;\n+    ngx_http_spdy_stream_t  *stream;\n+\n+    stream = frame->stream;\n+\n+    cl = frame->first;\n+\n+    if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame) {\n+\n+        if (cl->buf->pos != cl->buf->last) {\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"spdy:%ui DATA frame %p was sent partially\",\n+                           stream->id, frame);\n+\n+            return NGX_AGAIN;\n+        }\n+\n+        ln = cl->next;\n+\n+        cl->next = stream->free_data_headers;\n+        stream->free_data_headers = cl;\n+\n+        if (cl == frame->last) {\n+            goto done;\n+        }\n+\n+        cl = ln;\n+    }\n+\n+    for ( ;; ) {\n+        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {\n+            buf = cl->buf->shadow;\n+\n+            if (ngx_buf_in_memory(buf)) {\n+                buf->pos = cl->buf->pos;\n+            }\n+\n+            if (buf->in_file) {\n+                buf->file_pos = cl->buf->file_pos;\n+            }\n+        }\n+\n+        if (ngx_buf_size(cl->buf) != 0) {\n+\n+            if (cl != frame->first) {\n+                frame->first = cl;\n+                ngx_http_spdy_handle_stream(sc, stream);\n+            }\n+\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                           \"spdy:%ui DATA frame %p was sent partially\",\n+                           stream->id, frame);\n+\n+            return NGX_AGAIN;\n+        }\n+\n+        ln = cl->next;\n+\n+        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {\n+            cl->next = stream->free_bufs;\n+            stream->free_bufs = cl;\n+\n+        } else {\n+            ngx_free_chain(stream->request->pool, cl);\n+        }\n+\n+        if (cl == frame->last) {\n+            goto done;\n+        }\n+\n+        cl = ln;\n+    }\n+\n+done:\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,\n+                   \"spdy:%ui DATA frame %p was sent\", stream->id, frame);\n+\n+    stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE;\n+\n+    ngx_http_spdy_handle_frame(stream, frame);\n+\n+    ngx_http_spdy_handle_stream(sc, stream);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_inline void\n+ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_http_request_t  *r;\n+\n+    r = stream->request;\n+\n+    r->connection->sent += NGX_SPDY_FRAME_HEADER_SIZE + frame->length;\n+\n+    if (frame->fin) {\n+        stream->out_closed = 1;\n+    }\n+\n+    frame->next = stream->free_frames;\n+    stream->free_frames = frame;\n+\n+    stream->queued--;\n+}\n+\n+\n+static ngx_inline void\n+ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_stream_t *stream)\n+{\n+    ngx_event_t  *wev;\n+\n+    if (stream->handled || stream->blocked || stream->exhausted) {\n+        return;\n+    }\n+\n+    wev = stream->request->connection->write;\n+\n+    /*\n+     * This timer can only be set if the stream was delayed because of rate\n+     * limit.  In that case the event should be triggered by the timer.\n+     */\n+\n+    if (!wev->timer_set) {\n+        wev->delayed = 0;\n+\n+        stream->handled = 1;\n+        ngx_queue_insert_tail(&sc->posted, &stream->queue);\n+    }\n+}\n+\n+\n+static void\n+ngx_http_spdy_filter_cleanup(void *data)\n+{\n+    ngx_http_spdy_stream_t *stream = data;\n+\n+    size_t                       delta;\n+    ngx_http_spdy_out_frame_t   *frame, **fn;\n+    ngx_http_spdy_connection_t  *sc;\n+\n+    if (stream->handled) {\n+        stream->handled = 0;\n+        ngx_queue_remove(&stream->queue);\n+    }\n+\n+    if (stream->queued == 0) {\n+        return;\n+    }\n+\n+    delta = 0;\n+    sc = stream->connection;\n+    fn = &sc->last_out;\n+\n+    for ( ;; ) {\n+        frame = *fn;\n+\n+        if (frame == NULL) {\n+            break;\n+        }\n+\n+        if (frame->stream == stream && !frame->blocked) {\n+            *fn = frame->next;\n+\n+            delta += frame->length;\n+\n+            if (--stream->queued == 0) {\n+                break;\n+            }\n+\n+            continue;\n+        }\n+\n+        fn = &frame->next;\n+    }\n+\n+    if (sc->send_window == 0 && delta && !ngx_queue_empty(&sc->waiting)) {\n+        ngx_queue_add(&sc->posted, &sc->waiting);\n+        ngx_queue_init(&sc->waiting);\n+    }\n+\n+    sc->send_window += delta;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_filter_init(ngx_conf_t *cf)\n+{\n+    ngx_http_next_header_filter = ngx_http_top_header_filter;\n+    ngx_http_top_header_filter = ngx_http_spdy_header_filter;\n+\n+    return NGX_OK;\n+}\ndiff -uNr a/src/http/ngx_http_spdy.h b/src/http/ngx_http_spdy.h\n--- a/src/http/ngx_http_spdy.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy.h\t2020-01-03 11:57:09.000000000 +0800\n@@ -0,0 +1,261 @@\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#ifndef _NGX_HTTP_SPDY_H_INCLUDED_\n+#define _NGX_HTTP_SPDY_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+\n+#include <zlib.h>\n+\n+\n+#define NGX_SPDY_VERSION              3\n+\n+#define NGX_SPDY_NPN_ADVERTISE        \"\\x08spdy/3.1\"\n+#define NGX_SPDY_NPN_NEGOTIATED       \"spdy/3.1\"\n+\n+#define NGX_SPDY_STATE_BUFFER_SIZE    16\n+\n+#define NGX_SPDY_CTL_BIT              1\n+\n+#define NGX_SPDY_SYN_STREAM           1\n+#define NGX_SPDY_SYN_REPLY            2\n+#define NGX_SPDY_RST_STREAM           3\n+#define NGX_SPDY_SETTINGS             4\n+#define NGX_SPDY_PING                 6\n+#define NGX_SPDY_GOAWAY               7\n+#define NGX_SPDY_HEADERS              8\n+#define NGX_SPDY_WINDOW_UPDATE        9\n+\n+#define NGX_SPDY_FRAME_HEADER_SIZE    8\n+\n+#define NGX_SPDY_SID_SIZE             4\n+#define NGX_SPDY_DELTA_SIZE           4\n+\n+#define NGX_SPDY_SYN_STREAM_SIZE      10\n+#define NGX_SPDY_SYN_REPLY_SIZE       4\n+#define NGX_SPDY_RST_STREAM_SIZE      8\n+#define NGX_SPDY_PING_SIZE            4\n+#define NGX_SPDY_GOAWAY_SIZE          8\n+#define NGX_SPDY_WINDOW_UPDATE_SIZE   8\n+#define NGX_SPDY_NV_NUM_SIZE          4\n+#define NGX_SPDY_NV_NLEN_SIZE         4\n+#define NGX_SPDY_NV_VLEN_SIZE         4\n+#define NGX_SPDY_SETTINGS_NUM_SIZE    4\n+#define NGX_SPDY_SETTINGS_FID_SIZE    4\n+#define NGX_SPDY_SETTINGS_VAL_SIZE    4\n+\n+#define NGX_SPDY_SETTINGS_PAIR_SIZE                                           \\\n+    (NGX_SPDY_SETTINGS_FID_SIZE + NGX_SPDY_SETTINGS_VAL_SIZE)\n+\n+#define NGX_SPDY_HIGHEST_PRIORITY     0\n+#define NGX_SPDY_LOWEST_PRIORITY      7\n+\n+#define NGX_SPDY_FLAG_FIN             0x01\n+#define NGX_SPDY_FLAG_UNIDIRECTIONAL  0x02\n+#define NGX_SPDY_FLAG_CLEAR_SETTINGS  0x01\n+\n+#define NGX_SPDY_MAX_FRAME_SIZE       ((1 << 24) - 1)\n+\n+#define NGX_SPDY_DATA_DISCARD         1\n+#define NGX_SPDY_DATA_ERROR           2\n+#define NGX_SPDY_DATA_INTERNAL_ERROR  3\n+\n+\n+typedef struct ngx_http_spdy_connection_s   ngx_http_spdy_connection_t;\n+typedef struct ngx_http_spdy_out_frame_s    ngx_http_spdy_out_frame_t;\n+\n+\n+typedef u_char *(*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc,\n+    u_char *pos, u_char *end);\n+\n+struct ngx_http_spdy_connection_s {\n+    ngx_connection_t                *connection;\n+    ngx_http_connection_t           *http_connection;\n+\n+    ngx_uint_t                       processing;\n+\n+    size_t                           send_window;\n+    size_t                           recv_window;\n+    size_t                           init_window;\n+\n+    ngx_queue_t                      waiting;\n+\n+    u_char                           buffer[NGX_SPDY_STATE_BUFFER_SIZE];\n+    size_t                           buffer_used;\n+    ngx_http_spdy_handler_pt         handler;\n+\n+    z_stream                         zstream_in;\n+    z_stream                         zstream_out;\n+\n+    ngx_pool_t                      *pool;\n+\n+    ngx_http_spdy_out_frame_t       *free_ctl_frames;\n+    ngx_connection_t                *free_fake_connections;\n+\n+    ngx_http_spdy_stream_t         **streams_index;\n+\n+    ngx_http_spdy_out_frame_t       *last_out;\n+\n+    ngx_queue_t                      posted;\n+\n+    ngx_http_spdy_stream_t          *stream;\n+\n+    ngx_uint_t                       entries;\n+    size_t                           length;\n+    u_char                           flags;\n+\n+    ngx_uint_t                       last_sid;\n+\n+    unsigned                         blocked:1;\n+    unsigned                         incomplete:1;\n+};\n+\n+\n+struct ngx_http_spdy_stream_s {\n+    ngx_uint_t                       id;\n+    ngx_http_request_t              *request;\n+    ngx_http_spdy_connection_t      *connection;\n+    ngx_http_spdy_stream_t          *index;\n+\n+    ngx_uint_t                       header_buffers;\n+    ngx_uint_t                       queued;\n+\n+    /*\n+     * A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the\n+     * send_window to become negative, hence it's signed.\n+     */\n+    ssize_t                          send_window;\n+    size_t                           recv_window;\n+\n+    ngx_http_spdy_out_frame_t       *free_frames;\n+    ngx_chain_t                     *free_data_headers;\n+    ngx_chain_t                     *free_bufs;\n+\n+    ngx_queue_t                      queue;\n+\n+    unsigned                         priority:3;\n+    unsigned                         handled:1;\n+    unsigned                         blocked:1;\n+    unsigned                         exhausted:1;\n+    unsigned                         in_closed:1;\n+    unsigned                         out_closed:1;\n+    unsigned                         skip_data:2;\n+};\n+\n+\n+struct ngx_http_spdy_out_frame_s {\n+    ngx_http_spdy_out_frame_t       *next;\n+    ngx_chain_t                     *first;\n+    ngx_chain_t                     *last;\n+    ngx_int_t                      (*handler)(ngx_http_spdy_connection_t *sc,\n+                                        ngx_http_spdy_out_frame_t *frame);\n+\n+    ngx_http_spdy_stream_t          *stream;\n+    size_t                           length;\n+\n+    ngx_uint_t                       priority;\n+    unsigned                         blocked:1;\n+    unsigned                         fin:1;\n+};\n+\n+\n+static ngx_inline void\n+ngx_http_spdy_queue_frame(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_http_spdy_out_frame_t  **out;\n+\n+    for (out = &sc->last_out; *out; out = &(*out)->next)\n+    {\n+        /*\n+         * NB: higher values represent lower priorities.\n+         */\n+        if (frame->priority >= (*out)->priority) {\n+            break;\n+        }\n+    }\n+\n+    frame->next = *out;\n+    *out = frame;\n+}\n+\n+\n+static ngx_inline void\n+ngx_http_spdy_queue_blocked_frame(ngx_http_spdy_connection_t *sc,\n+    ngx_http_spdy_out_frame_t *frame)\n+{\n+    ngx_http_spdy_out_frame_t  **out;\n+\n+    for (out = &sc->last_out; *out; out = &(*out)->next)\n+    {\n+        if ((*out)->blocked) {\n+            break;\n+        }\n+    }\n+\n+    frame->next = *out;\n+    *out = frame;\n+}\n+\n+\n+void ngx_http_spdy_init(ngx_event_t *rev);\n+void ngx_http_spdy_request_headers_init(void);\n+\n+ngx_int_t ngx_http_spdy_read_request_body(ngx_http_request_t *r,\n+    ngx_http_client_body_handler_pt post_handler);\n+\n+void ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc);\n+\n+ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc);\n+\n+\n+#define ngx_spdy_frame_aligned_write_uint16(p, s)                             \\\n+    (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))\n+\n+#define ngx_spdy_frame_aligned_write_uint32(p, s)                             \\\n+    (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))\n+\n+#if (NGX_HAVE_NONALIGNED)\n+\n+#define ngx_spdy_frame_write_uint16  ngx_spdy_frame_aligned_write_uint16\n+#define ngx_spdy_frame_write_uint32  ngx_spdy_frame_aligned_write_uint32\n+\n+#else\n+\n+#define ngx_spdy_frame_write_uint16(p, s)                                     \\\n+    ((p)[0] = (u_char) ((s) >> 8),                                            \\\n+     (p)[1] = (u_char)  (s),                                                  \\\n+     (p) + sizeof(uint16_t))\n+\n+#define ngx_spdy_frame_write_uint32(p, s)                                     \\\n+    ((p)[0] = (u_char) ((s) >> 24),                                           \\\n+     (p)[1] = (u_char) ((s) >> 16),                                           \\\n+     (p)[2] = (u_char) ((s) >> 8),                                            \\\n+     (p)[3] = (u_char)  (s),                                                  \\\n+     (p) + sizeof(uint32_t))\n+\n+#endif\n+\n+\n+#define ngx_spdy_ctl_frame_head(t)                                            \\\n+    ((uint32_t) NGX_SPDY_CTL_BIT << 31 | NGX_SPDY_VERSION << 16 | (t))\n+\n+#define ngx_spdy_frame_write_head(p, t)                                       \\\n+    ngx_spdy_frame_aligned_write_uint32(p, ngx_spdy_ctl_frame_head(t))\n+\n+#define ngx_spdy_frame_write_flags_and_len(p, f, l)                           \\\n+    ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (l))\n+#define ngx_spdy_frame_write_flags_and_id(p, f, i)                            \\\n+    ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (i))\n+\n+#define ngx_spdy_frame_write_sid     ngx_spdy_frame_aligned_write_uint32\n+#define ngx_spdy_frame_write_window  ngx_spdy_frame_aligned_write_uint32\n+\n+#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */\ndiff -uNr a/src/http/ngx_http_spdy_module.c b/src/http/ngx_http_spdy_module.c\n--- a/src/http/ngx_http_spdy_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy_module.c\t2020-01-03 11:59:02.000000000 +0800\n@@ -0,0 +1,408 @@\n+\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_spdy_module.h>\n+\n+\n+static ngx_int_t ngx_http_spdy_add_variables(ngx_conf_t *cf);\n+\n+static ngx_int_t ngx_http_spdy_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data);\n+static ngx_int_t ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data);\n+\n+static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle);\n+\n+static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf);\n+static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf);\n+static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf);\n+static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent,\n+    void *child);\n+static void *ngx_http_spdy_create_loc_conf(ngx_conf_t *cf);\n+static char *ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent,\n+    void *child);\n+\n+static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post,\n+    void *data);\n+static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data);\n+static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post,\n+    void *data);\n+static char *ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data);\n+\n+\n+static ngx_conf_num_bounds_t  ngx_http_spdy_headers_comp_bounds = {\n+    ngx_conf_check_num_bounds, 0, 9\n+};\n+\n+static ngx_conf_post_t  ngx_http_spdy_recv_buffer_size_post =\n+    { ngx_http_spdy_recv_buffer_size };\n+static ngx_conf_post_t  ngx_http_spdy_pool_size_post =\n+    { ngx_http_spdy_pool_size };\n+static ngx_conf_post_t  ngx_http_spdy_streams_index_mask_post =\n+    { ngx_http_spdy_streams_index_mask };\n+static ngx_conf_post_t  ngx_http_spdy_chunk_size_post =\n+    { ngx_http_spdy_chunk_size };\n+\n+\n+static ngx_command_t  ngx_http_spdy_commands[] = {\n+\n+    { ngx_string(\"spdy_recv_buffer_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_MAIN_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_main_conf_t, recv_buffer_size),\n+      &ngx_http_spdy_recv_buffer_size_post },\n+\n+    { ngx_string(\"spdy_pool_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, pool_size),\n+      &ngx_http_spdy_pool_size_post },\n+\n+    { ngx_string(\"spdy_max_concurrent_streams\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, concurrent_streams),\n+      NULL },\n+\n+    { ngx_string(\"spdy_streams_index_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, streams_index_mask),\n+      &ngx_http_spdy_streams_index_mask_post },\n+\n+    { ngx_string(\"spdy_recv_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, recv_timeout),\n+      NULL },\n+\n+    { ngx_string(\"spdy_keepalive_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, keepalive_timeout),\n+      NULL },\n+\n+    { ngx_string(\"spdy_headers_comp\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_srv_conf_t, headers_comp),\n+      &ngx_http_spdy_headers_comp_bounds },\n+\n+    { ngx_string(\"spdy_chunk_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_LOC_CONF_OFFSET,\n+      offsetof(ngx_http_spdy_loc_conf_t, chunk_size),\n+      &ngx_http_spdy_chunk_size_post },\n+\n+      ngx_null_command\n+};\n+\n+\n+static ngx_http_module_t  ngx_http_spdy_module_ctx = {\n+    ngx_http_spdy_add_variables,           /* preconfiguration */\n+    NULL,                                  /* postconfiguration */\n+\n+    ngx_http_spdy_create_main_conf,        /* create main configuration */\n+    ngx_http_spdy_init_main_conf,          /* init main configuration */\n+\n+    ngx_http_spdy_create_srv_conf,         /* create server configuration */\n+    ngx_http_spdy_merge_srv_conf,          /* merge server configuration */\n+\n+    ngx_http_spdy_create_loc_conf,         /* create location configuration */\n+    ngx_http_spdy_merge_loc_conf           /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_spdy_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_spdy_module_ctx,             /* module context */\n+    ngx_http_spdy_commands,                /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    ngx_http_spdy_module_init,             /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_variable_t  ngx_http_spdy_vars[] = {\n+\n+    { ngx_string(\"spdy\"), NULL,\n+      ngx_http_spdy_variable, 0, 0, 0 },\n+\n+    { ngx_string(\"spdy_request_priority\"), NULL,\n+      ngx_http_spdy_request_priority_variable, 0, 0, 0 },\n+\n+    { ngx_null_string, NULL, NULL, 0, 0, 0 }\n+};\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_add_variables(ngx_conf_t *cf)\n+{\n+   ngx_http_variable_t  *var, *v;\n+\n+    for (v = ngx_http_spdy_vars; v->name.len; v++) {\n+        var = ngx_http_add_variable(cf, &v->name, v->flags);\n+        if (var == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        var->get_handler = v->get_handler;\n+        var->data = v->data;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data)\n+{\n+    if (r->spdy_stream) {\n+        v->len = sizeof(\"3.1\") - 1;\n+        v->valid = 1;\n+        v->no_cacheable = 0;\n+        v->not_found = 0;\n+        v->data = (u_char *) \"3.1\";\n+\n+        return NGX_OK;\n+    }\n+\n+    *v = ngx_http_variable_null_value;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data)\n+{\n+    if (r->spdy_stream) {\n+        v->len = 1;\n+        v->valid = 1;\n+        v->no_cacheable = 0;\n+        v->not_found = 0;\n+\n+        v->data = ngx_pnalloc(r->pool, 1);\n+        if (v->data == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        v->data[0] = '0' + (u_char) r->spdy_stream->priority;\n+\n+        return NGX_OK;\n+    }\n+\n+    *v = ngx_http_variable_null_value;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_spdy_module_init(ngx_cycle_t *cycle)\n+{\n+    ngx_http_spdy_request_headers_init();\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void *\n+ngx_http_spdy_create_main_conf(ngx_conf_t *cf)\n+{\n+    ngx_http_spdy_main_conf_t  *smcf;\n+\n+    smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_main_conf_t));\n+    if (smcf == NULL) {\n+        return NULL;\n+    }\n+\n+    smcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;\n+\n+    return smcf;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf)\n+{\n+    ngx_http_spdy_main_conf_t *smcf = conf;\n+\n+    ngx_conf_init_size_value(smcf->recv_buffer_size, 256 * 1024);\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static void *\n+ngx_http_spdy_create_srv_conf(ngx_conf_t *cf)\n+{\n+    ngx_http_spdy_srv_conf_t  *sscf;\n+\n+    sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_srv_conf_t));\n+    if (sscf == NULL) {\n+        return NULL;\n+    }\n+\n+    sscf->pool_size = NGX_CONF_UNSET_SIZE;\n+\n+    sscf->concurrent_streams = NGX_CONF_UNSET_UINT;\n+    sscf->streams_index_mask = NGX_CONF_UNSET_UINT;\n+\n+    sscf->recv_timeout = NGX_CONF_UNSET_MSEC;\n+    sscf->keepalive_timeout = NGX_CONF_UNSET_MSEC;\n+\n+    sscf->headers_comp = NGX_CONF_UNSET;\n+\n+    return sscf;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n+{\n+    ngx_http_spdy_srv_conf_t *prev = parent;\n+    ngx_http_spdy_srv_conf_t *conf = child;\n+\n+    ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);\n+\n+    ngx_conf_merge_uint_value(conf->concurrent_streams,\n+                              prev->concurrent_streams, 100);\n+\n+    ngx_conf_merge_uint_value(conf->streams_index_mask,\n+                              prev->streams_index_mask, 32 - 1);\n+\n+    ngx_conf_merge_msec_value(conf->recv_timeout,\n+                              prev->recv_timeout, 30000);\n+    ngx_conf_merge_msec_value(conf->keepalive_timeout,\n+                              prev->keepalive_timeout, 180000);\n+\n+    ngx_conf_merge_value(conf->headers_comp, prev->headers_comp, 0);\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static void *\n+ngx_http_spdy_create_loc_conf(ngx_conf_t *cf)\n+{\n+    ngx_http_spdy_loc_conf_t  *slcf;\n+\n+    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_loc_conf_t));\n+    if (slcf == NULL) {\n+        return NULL;\n+    }\n+\n+    slcf->chunk_size = NGX_CONF_UNSET_SIZE;\n+\n+    return slcf;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n+{\n+    ngx_http_spdy_loc_conf_t *prev = parent;\n+    ngx_http_spdy_loc_conf_t *conf = child;\n+\n+    ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)\n+{\n+    size_t *sp = data;\n+\n+    if (*sp <= 2 * NGX_SPDY_STATE_BUFFER_SIZE) {\n+        return \"value is too small\";\n+    }\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data)\n+{\n+    size_t *sp = data;\n+\n+    if (*sp < NGX_MIN_POOL_SIZE) {\n+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                           \"the pool size must be no less than %uz\",\n+                           NGX_MIN_POOL_SIZE);\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    if (*sp % NGX_POOL_ALIGNMENT) {\n+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                           \"the pool size must be a multiple of %uz\",\n+                           NGX_POOL_ALIGNMENT);\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data)\n+{\n+    ngx_uint_t *np = data;\n+\n+    ngx_uint_t  mask;\n+\n+    mask = *np - 1;\n+\n+    if (*np == 0 || (*np & mask)) {\n+        return \"must be a power of two\";\n+    }\n+\n+    *np = mask;\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static char *\n+ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data)\n+{\n+    size_t *sp = data;\n+\n+    if (*sp == 0) {\n+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                           \"the spdy chunk size cannot be zero\");\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    if (*sp > NGX_SPDY_MAX_FRAME_SIZE) {\n+        *sp = NGX_SPDY_MAX_FRAME_SIZE;\n+    }\n+\n+    return NGX_CONF_OK;\n+}\ndiff -uNr a/src/http/ngx_http_spdy_module.h b/src/http/ngx_http_spdy_module.h\n--- a/src/http/ngx_http_spdy_module.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/ngx_http_spdy_module.h\t2020-01-03 12:00:23.000000000 +0800\n@@ -0,0 +1,41 @@\n+\n+/*\n+ * Copyright (C) Nginx, Inc.\n+ * Copyright (C) Valentin V. Bartenev\n+ */\n+\n+\n+#ifndef _NGX_HTTP_SPDY_MODULE_H_INCLUDED_\n+#define _NGX_HTTP_SPDY_MODULE_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+\n+\n+typedef struct {\n+    size_t                          recv_buffer_size;\n+    u_char                         *recv_buffer;\n+} ngx_http_spdy_main_conf_t;\n+\n+\n+typedef struct {\n+    size_t                          pool_size;\n+    ngx_uint_t                      concurrent_streams;\n+    ngx_uint_t                      streams_index_mask;\n+    ngx_msec_t                      recv_timeout;\n+    ngx_msec_t                      keepalive_timeout;\n+    ngx_int_t                       headers_comp;\n+} ngx_http_spdy_srv_conf_t;\n+\n+\n+typedef struct {\n+    size_t                          chunk_size;\n+} ngx_http_spdy_loc_conf_t;\n+\n+\n+extern ngx_module_t  ngx_http_spdy_module;\n+\n+\n+#endif /* _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ */\ndiff -uNr a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c\n--- a/src/http/ngx_http_upstream.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/ngx_http_upstream.c\t2020-01-03 12:02:16.000000000 +0800\n@@ -525,6 +525,18 @@\n         return;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        ngx_http_upstream_init_request(r);\n+        return;\n+    }\n+#endif\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        ngx_http_upstream_init_request(r);\n+        return;\n+    }\n+#endif\n \n     if (c->read->timer_set) {\n         ngx_del_timer(c->read);\n@@ -1347,6 +1359,16 @@\n         return;\n     }\n #endif\n+#if (NGX_HTTP_SPDY)\n+    if (r->spdy_stream) {\n+        return;\n+    }\n+#endif\n+#if (NGX_HTTP_V3)\n+    if (r->qstream) {\n+        return;\n+    }\n+#endif\n \n #if (NGX_HAVE_KQUEUE)\n \ndiff -uNr a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c\n--- a/src/http/v2/ngx_http_v2.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -271,6 +271,8 @@\n \n     h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n \n+    h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE;\n+\n     h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n \n     h2c->concurrent_pushes = h2scf->concurrent_pushes;\n@@ -2093,6 +2095,14 @@\n         case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:\n \n             h2c->table_update = 1;\n+\n+            if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+                h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE;\n+            } else {\n+                h2c->max_hpack_table_size = value;\n+            }\n+\n+            h2c->indicate_resize = 1;\n             break;\n \n         default:\ndiff -uNr a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c\n--- a/src/http/v2/ngx_http_v2_encode.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_encode.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -10,7 +10,7 @@\n #include <ngx_http.h>\n \n \n-static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n+u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n     ngx_uint_t value);\n \n \n@@ -40,7 +40,7 @@\n }\n \n \n-static u_char *\n+u_char *\n ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)\n {\n     if (value < prefix) {\ndiff -uNr a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c\n--- a/src/http/v2/ngx_http_v2_filter_module.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_filter_module.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -23,10 +23,53 @@\n #define ngx_http_v2_literal_size(h)                                           \\\n     (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)\n \n+#define ngx_http_v2_indexed(i)      (128 + (i))\n+#define ngx_http_v2_inc_indexed(i)  (64 + (i))\n+\n+#define NGX_HTTP_V2_ENCODE_RAW            0\n+#define NGX_HTTP_V2_ENCODE_HUFF           0x80\n+\n+#define NGX_HTTP_V2_AUTHORITY_INDEX       1\n+#define NGX_HTTP_V2_METHOD_GET_INDEX      2\n+#define NGX_HTTP_V2_PATH_INDEX            4\n+\n+#define NGX_HTTP_V2_SCHEME_HTTP_INDEX     6\n+#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX    7\n+\n+#define NGX_HTTP_V2_STATUS_INDEX          8\n+#define NGX_HTTP_V2_STATUS_200_INDEX      8\n+#define NGX_HTTP_V2_STATUS_204_INDEX      9\n+#define NGX_HTTP_V2_STATUS_206_INDEX      10\n+#define NGX_HTTP_V2_STATUS_304_INDEX      11\n+#define NGX_HTTP_V2_STATUS_400_INDEX      12\n+#define NGX_HTTP_V2_STATUS_404_INDEX      13\n+#define NGX_HTTP_V2_STATUS_500_INDEX      14\n+\n+#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16\n+#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17\n+#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX  28\n+#define NGX_HTTP_V2_CONTENT_TYPE_INDEX    31\n+#define NGX_HTTP_V2_DATE_INDEX            33\n+#define NGX_HTTP_V2_LAST_MODIFIED_INDEX   44\n+#define NGX_HTTP_V2_LOCATION_INDEX        46\n+#define NGX_HTTP_V2_SERVER_INDEX          54\n+#define NGX_HTTP_V2_USER_AGENT_INDEX      58\n+#define NGX_HTTP_V2_VARY_INDEX            59\n \n #define NGX_HTTP_V2_NO_TRAILERS           (ngx_http_v2_out_frame_t *) -1\n \n \n+static const struct {\n+    u_char        *name;\n+    u_char const   len;\n+} push_header[] = {\n+    { (u_char*)\":authority\"      , 10 },\n+    { (u_char*)\"accept-encoding\" , 15 },\n+    { (u_char*)\"accept-language\" , 15 },\n+    { (u_char*)\"user-agent\"      , 10 }\n+};\n+\n+\n typedef struct {\n     ngx_str_t      name;\n     u_char         index;\n@@ -155,11 +198,9 @@\n #endif\n \n     static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);\n-    static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];\n \n     static size_t nginx_ver_build_len =\n                                   ngx_http_v2_literal_size(NGINX_VER_BUILD);\n-    static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];\n \n     stream = r->stream;\n \n@@ -435,7 +476,7 @@\n     }\n \n     tmp = ngx_palloc(r->pool, tmp_len);\n-    pos = ngx_pnalloc(r->pool, len);\n+    pos = ngx_pnalloc(r->pool, len + 15 + 1);\n \n     if (pos == NULL || tmp == NULL) {\n         return NGX_ERROR;\n@@ -443,11 +484,16 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n     }\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n@@ -458,67 +504,28 @@\n         *pos++ = status;\n \n     } else {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);\n-        *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;\n-        pos = ngx_sprintf(pos, \"%03ui\", r->headers_out.status);\n+        ngx_sprintf(pos + 8, \"%O3ui\", r->headers_out.status);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\":status\",\n+                                       sizeof(\":status\") - 1, pos + 8, 3, tmp);\n     }\n \n     if (r->headers_out.server == NULL) {\n-\n         if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER);\n-\n-        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: %s\\\"\",\n-                           NGINX_VER_BUILD);\n-\n-        } else {\n-            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"server: nginx\\\"\");\n-        }\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);\n-\n-        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n-            if (nginx_ver[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,\n-                                            sizeof(NGINX_VER) - 1, tmp);\n-                nginx_ver_len = p - nginx_ver;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER);\n \n         } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n-            if (nginx_ver_build[0] == '\\0') {\n-                p = ngx_http_v2_write_value(nginx_ver_build,\n-                                            (u_char *) NGINX_VER_BUILD,\n-                                            sizeof(NGINX_VER_BUILD) - 1, tmp);\n-                nginx_ver_build_len = p - nginx_ver_build;\n-            }\n-\n-            pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);\n+            pos = ngx_http_v2_write_header_str(\"server\", NGINX_VER_BUILD);\n \n         } else {\n-            pos = ngx_cpymem(pos, nginx, sizeof(nginx));\n+            pos = ngx_http_v2_write_header_str(\"server\", \"nginx\");\n         }\n     }\n \n     if (r->headers_out.date == NULL) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"date: %V\\\"\",\n-                       &ngx_cached_http_time);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);\n-        pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,\n-                                      ngx_cached_http_time.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"date\", ngx_cached_http_time);\n     }\n \n     if (r->headers_out.content_type.len) {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);\n-\n         if (r->headers_out.content_type_len == r->headers_out.content_type.len\n             && r->headers_out.charset.len)\n         {\n@@ -544,64 +551,36 @@\n             r->headers_out.content_type.data = p - len;\n         }\n \n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-type: %V\\\"\",\n-                       &r->headers_out.content_type);\n-\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,\n-                                      r->headers_out.content_type.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"content-type\",\n+                                           r->headers_out.content_type);\n     }\n \n     if (r->headers_out.content_length == NULL\n         && r->headers_out.content_length_n >= 0)\n     {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"content-length: %O\\\"\",\n-                       r->headers_out.content_length_n);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);\n-\n-        p = pos;\n-        pos = ngx_sprintf(pos + 1, \"%O\", r->headers_out.content_length_n);\n-        *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);\n+        p = ngx_sprintf(pos + 15, \"%O\", r->headers_out.content_length_n);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"content-length\",\n+                                       sizeof(\"content-length\") - 1, pos + 15,\n+                                       p - (pos + 15), tmp);\n     }\n \n     if (r->headers_out.last_modified == NULL\n         && r->headers_out.last_modified_time != -1)\n     {\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);\n-\n-        ngx_http_time(pos, r->headers_out.last_modified_time);\n+        ngx_http_time(pos + 14, r->headers_out.last_modified_time);\n         len = sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1;\n-\n-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"last-modified: %*s\\\"\",\n-                       len, pos);\n-\n-        /*\n-         * Date will always be encoded using huffman in the temporary buffer,\n-         * so it's safe here to use src and dst pointing to the same address.\n-         */\n-        pos = ngx_http_v2_write_value(pos, pos, len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, (u_char *)\"last-modified\",\n+                                       sizeof(\"last-modified\") - 1, pos + 14,\n+                                       len, tmp);\n     }\n \n     if (r->headers_out.location && r->headers_out.location->value.len) {\n-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"location: %V\\\"\",\n-                       &r->headers_out.location->value);\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);\n-        pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,\n-                                      r->headers_out.location->value.len, tmp);\n+        pos = ngx_http_v2_write_header_tbl(\"location\", r->headers_out.location->value);\n     }\n \n #if (NGX_HTTP_GZIP)\n     if (r->gzip_vary) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 output header: \\\"vary: Accept-Encoding\\\"\");\n-\n-        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);\n-        pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));\n+        pos = ngx_http_v2_write_header_str(\"vary\", \"Accept-Encoding\");\n     }\n #endif\n \n@@ -624,23 +603,10 @@\n             continue;\n         }\n \n-#if (NGX_DEBUG)\n-        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n-            ngx_strlow(tmp, header[i].key.data, header[i].key.len);\n-\n-            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                           \"http2 output header: \\\"%*s: %V\\\"\",\n-                           header[i].key.len, tmp, &header[i].value);\n-        }\n-#endif\n-\n-        *pos++ = 0;\n-\n-        pos = ngx_http_v2_write_name(pos, header[i].key.data,\n-                                     header[i].key.len, tmp);\n+        pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data,\n+                                       header[i].key.len, header[i].value.data,\n+                                       header[i].value.len, tmp);\n \n-        pos = ngx_http_v2_write_value(pos, header[i].value.data,\n-                                      header[i].value.len, tmp);\n     }\n \n     fin = r->header_only\n@@ -998,6 +964,7 @@\n \n     for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n         len += binary[i].len;\n+        len += push_header[i].len + 1;\n     }\n \n     pos = ngx_pnalloc(r->pool, len);\n@@ -1007,12 +974,17 @@\n \n     start = pos;\n \n-    if (h2c->table_update) {\n-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n-                       \"http2 table size update: 0\");\n-        *pos++ = (1 << 5) | 0;\n-        h2c->table_update = 0;\n-    }\n+    h2c = r->stream->connection;\n+\n+    if (h2c->indicate_resize) {\n+        *pos = 32;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),\n+                                    h2c->max_hpack_table_size);\n+        h2c->indicate_resize = 0;\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+        ngx_http_v2_table_resize(h2c);\n+#endif\n+     }\n \n     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":method: GET\\\"\");\n@@ -1022,8 +994,7 @@\n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":path: %V\\\"\", path);\n \n-    *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n-    pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);\n+    pos = ngx_http_v2_write_header_pot(\":path\", path);\n \n     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                    \"http2 push header: \\\":scheme: %V\\\"\", &r->schema);\n@@ -1048,11 +1019,15 @@\n             continue;\n         }\n \n+        value = &(*h)->value;\n+\n         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                        \"http2 push header: \\\"%V: %V\\\"\",\n                        &ph[i].name, &(*h)->value);\n \n-        pos = ngx_cpymem(pos, binary[i].data, binary[i].len);\n+        pos = ngx_http_v2_write_header(h2c, pos,\n+                  push_header[i].name, push_header[i].len, value->data, value->len,\n+                  tmp);\n     }\n \n     frame = ngx_http_v2_create_push_frame(r, start, pos);\ndiff -uNr a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h\n--- a/src/http/v2/ngx_http_v2.h\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2.h\t2020-01-02 21:25:20.000000000 +0800\n@@ -52,6 +52,14 @@\n #define NGX_HTTP_V2_MAX_WINDOW           ((1U << 31) - 1)\n #define NGX_HTTP_V2_DEFAULT_WINDOW       65535\n \n+#define HPACK_ENC_HTABLE_SZ              128 /* better to keep a PoT < 64k */\n+#define HPACK_ENC_HTABLE_ENTRIES         ((HPACK_ENC_HTABLE_SZ * 100) / 128)\n+#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ     10  /* 10 is sufficient for most */\n+#define HPACK_ENC_MAX_ENTRY              512 /* longest header size to match */\n+\n+#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE     4096\n+#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE         16384 /* < 64k */\n+\n #define NGX_HTTP_V2_DEFAULT_WEIGHT       16\n \n \n@@ -115,6 +123,46 @@\n } ngx_http_v2_hpack_t;\n \n \n+#if (NGX_HTTP_V2_HPACK_ENC)\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen, vlen;\n+    uint16_t                         size;\n+    uint16_t                         next;\n+} ngx_http_v2_hpack_enc_entry_t;\n+\n+\n+typedef struct {\n+    uint64_t                         hash_val;\n+    uint32_t                         index;\n+    uint16_t                         pos;\n+    uint16_t                         klen;\n+} ngx_http_v2_hpack_name_entry_t;\n+\n+\n+typedef struct {\n+    size_t                           size;    /* size as defined in RFC 7541 */\n+    uint32_t                         top;     /* the last entry */\n+    uint32_t                         pos;\n+    uint16_t                         n_elems; /* number of elements */\n+    uint16_t                         base;    /* index of the oldest entry */\n+    uint16_t                         last;    /* index of the newest entry */\n+\n+    /* hash table for dynamic entries, instead using a generic hash table,\n+       which would be too slow to process a significant amount of headers,\n+       this table is not determenistic, and might ocasionally fail to insert\n+       a value, at the cost of slightly worse compression, but significantly\n+       faster performance */\n+    ngx_http_v2_hpack_enc_entry_t    htable[HPACK_ENC_HTABLE_SZ];\n+    ngx_http_v2_hpack_name_entry_t   heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ];\n+    u_char                           storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE +\n+                                             HPACK_ENC_MAX_ENTRY];\n+} ngx_http_v2_hpack_enc_t;\n+#endif\n+\n+\n struct ngx_http_v2_connection_s {\n     ngx_connection_t                *connection;\n     ngx_http_connection_t           *http_connection;\n@@ -136,6 +184,8 @@\n \n     size_t                           frame_size;\n \n+    size_t                           max_hpack_table_size;\n+\n     ngx_queue_t                      waiting;\n \n     ngx_http_v2_state_t              state;\n@@ -163,6 +213,11 @@\n     unsigned                         blocked:1;\n     unsigned                         goaway:1;\n     unsigned                         push_disabled:1;\n+    unsigned                         indicate_resize:1;\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+    ngx_http_v2_hpack_enc_t          hpack_enc;\n+#endif\n };\n \n \n@@ -206,6 +261,8 @@\n \n     ngx_array_t                     *cookies;\n \n+    size_t                           header_limit;\n+\n     ngx_pool_t                      *pool;\n \n     unsigned                         waiting:1;\n@@ -418,4 +475,35 @@\n     u_char *tmp, ngx_uint_t lower);\n \n \n+u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,\n+    u_char *tmp, ngx_uint_t lower);\n+\n+u_char *\n+ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value);\n+\n+#define ngx_http_v2_write_name(dst, src, len, tmp)                            \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 1)\n+#define ngx_http_v2_write_value(dst, src, len, tmp)                           \\\n+    ngx_http_v2_string_encode(dst, src, len, tmp, 0)\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+    u_char *key, size_t key_len, u_char *value, size_t value_len,\n+    u_char *tmp);\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c);\n+\n+#define ngx_http_v2_write_header_str(key, value)                        \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    (u_char *) value, sizeof(value) - 1, tmp);\n+\n+#define ngx_http_v2_write_header_tbl(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val.data, val.len, tmp);\n+\n+#define ngx_http_v2_write_header_pot(key, val)                          \\\n+    ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \\\n+    val->data, val->len, tmp);\n+\n #endif /* _NGX_HTTP_V2_H_INCLUDED_ */\ndiff -uNr a/src/http/v2/ngx_http_v2_module.c b/src/http/v2/ngx_http_v2_module.c\n--- a/src/http/v2/ngx_http_v2_module.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_module.c\t2020-01-03 12:04:35.000000000 +0800\n@@ -36,8 +36,6 @@\n static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post,\n     void *data);\n static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data);\n-static char *ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd,\n-    void *conf);\n \n \n static ngx_conf_post_t  ngx_http_v2_recv_buffer_size_post =\n@@ -152,62 +150,6 @@\n       0,\n       NULL },\n \n-    { ngx_string(\"spdy_recv_buffer_size\"),\n-      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_MAIN_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_pool_size\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_max_concurrent_streams\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_streams_index_size\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_recv_timeout\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_keepalive_timeout\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_headers_comp\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_SRV_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n-    { ngx_string(\"spdy_chunk_size\"),\n-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n-      ngx_http_v2_spdy_deprecated,\n-      NGX_HTTP_LOC_CONF_OFFSET,\n-      0,\n-      NULL },\n-\n       ngx_null_command\n };\n \n@@ -599,12 +541,3 @@\n }\n \n \n-static char *\n-ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n-{\n-    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n-                       \"invalid directive \\\"%V\\\": ngx_http_spdy_module \"\n-                       \"was superseded by ngx_http_v2_module\", &cmd->name);\n-\n-    return NGX_CONF_OK;\n-}\ndiff -uNr a/src/http/v2/ngx_http_v2_table.c b/src/http/v2/ngx_http_v2_table.c\n--- a/src/http/v2/ngx_http_v2_table.c\t2019-12-24 23:00:09.000000000 +0800\n+++ b/src/http/v2/ngx_http_v2_table.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -361,3 +361,434 @@\n \n     return NGX_OK;\n }\n+\n+\n+#if (NGX_HTTP_V2_HPACK_ENC)\n+\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len);\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len);\n+\n+\n+void\n+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c)\n+{\n+    ngx_http_v2_hpack_enc_entry_t  *table;\n+    uint64_t                        idx;\n+\n+    table = h2c->hpack_enc.htable;\n+\n+    while (h2c->hpack_enc.size > h2c->max_hpack_table_size) {\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+}\n+\n+\n+/* checks if a header is in the hpack table - if so returns the table entry,\n+   otherwise encodes and inserts into the table and returns 0,\n+   if failed to insert into table, returns -1 */\n+static ngx_int_t\n+ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c,\n+    size_t key_len, size_t val_len, uint8_t *key, uint8_t *val,\n+    ngx_int_t *header_idx)\n+{\n+    uint64_t  hash_val, key_hash, idx, lru;\n+    int       i;\n+    size_t    size = key_len + val_len + 32;\n+    uint8_t  *storage = h2c->hpack_enc.storage;\n+\n+    ngx_http_v2_hpack_enc_entry_t   *table;\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+\n+    *header_idx = NGX_ERROR;\n+    /* step 1: compute the hash value of header */\n+    if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) {\n+        return NGX_ERROR;\n+    }\n+\n+    key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234);\n+    hash_val = ngx_murmur_hash2_64(val, val_len, key_hash);\n+\n+    if (hash_val == 0) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* step 2: check if full header in the table */\n+    idx = hash_val;\n+    i = -1;\n+    while (idx) {\n+         /* at most 8 locations are checked, but most will be done in 1 or 2 */\n+        table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ];\n+        if (table->hash_val == hash_val\n+            && table->klen == key_len\n+            && table->vlen == val_len\n+            && ngx_memcmp(key, storage + table->pos, key_len) == 0\n+            && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0)\n+        {\n+            return (h2c->hpack_enc.top - table->index) + 61;\n+        }\n+\n+        if (table->hash_val == 0 && i == -1) {\n+            i = idx % HPACK_ENC_HTABLE_SZ;\n+            break;\n+        }\n+\n+        idx >>= 8;\n+    }\n+\n+    /* step 3: check if key is in one of the tables */\n+    *header_idx = hpack_get_static_index(h2c, key, key_len);\n+\n+    if (i == -1) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (*header_idx == NGX_ERROR) {\n+        *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len);\n+    }\n+\n+    /* step 4: store the new entry */\n+    table =  h2c->hpack_enc.htable;\n+\n+    if (h2c->hpack_enc.top == 0xffffffff) {\n+        /* just to be on the safe side, avoid overflow */\n+        ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t));\n+    }\n+\n+    while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size)\n+           || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) {\n+        /* make space for the new entry first */\n+        idx = h2c->hpack_enc.base;\n+        h2c->hpack_enc.base = table[idx].next;\n+        h2c->hpack_enc.size -= table[idx].size;\n+        table[idx].hash_val = 0;\n+        h2c->hpack_enc.n_elems--;\n+    }\n+\n+    table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val,\n+                                               .index = h2c->hpack_enc.top,\n+                                               .pos = h2c->hpack_enc.pos,\n+                                               .klen = key_len,\n+                                               .vlen = val_len,\n+                                               .size = size,\n+                                               .next = 0};\n+\n+    table[h2c->hpack_enc.last].next = i;\n+    if (h2c->hpack_enc.n_elems == 0) {\n+        h2c->hpack_enc.base = i;\n+    }\n+\n+    h2c->hpack_enc.last = i;\n+    h2c->hpack_enc.top++;\n+    h2c->hpack_enc.size += size;\n+    h2c->hpack_enc.n_elems++;\n+\n+    /* update header name lookup */\n+    if (*header_idx == NGX_ERROR ) {\n+        lru = h2c->hpack_enc.top;\n+\n+        for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+\n+            name = &h2c->hpack_enc.heads[i];\n+\n+            if ( name->hash_val == 0 || (name->hash_val == key_hash\n+                && ngx_memcmp(storage + name->pos, key, key_len) == 0) )\n+            {\n+                name->hash_val = key_hash;\n+                name->pos = h2c->hpack_enc.pos;\n+                name->index = h2c->hpack_enc.top - 1;\n+                break;\n+            }\n+\n+            if (lru > name->index) {\n+                lru = name->index;\n+                idx = i;\n+            }\n+        }\n+\n+        if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) {\n+            name = &h2c->hpack_enc.heads[idx];\n+            name->hash_val = hash_val;\n+            name->pos = h2c->hpack_enc.pos;\n+            name->index = h2c->hpack_enc.top - 1;\n+        }\n+    }\n+\n+    ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len);\n+    ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len);\n+\n+    h2c->hpack_enc.pos += size;\n+    if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {\n+        h2c->hpack_enc.pos = 0;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_int_t idx, header_idx;\n+\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    /* attempt to find the value in the dynamic table */\n+    idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value,\n+                                           &header_idx);\n+\n+    if (idx > 0) {\n+        /* positive index indicates success */\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                       \"http2 hpack encode: Indexed Header Field: %ud\", idx);\n+\n+        *pos = 128;\n+        pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx);\n+\n+    } else {\n+\n+        if (header_idx == NGX_ERROR) { /* if key is not present */\n+\n+            if (idx == NGX_ERROR) {    /* if header was not added */\n+                *pos++ = 0;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — New Name\");\n+            } else {                   /* if header was added */\n+                *pos++ = 64;\n+\n+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — New Name\");\n+            }\n+\n+            pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+\n+        } else {                       /* if key is present */\n+\n+            if (idx == NGX_ERROR) {\n+                *pos = 0;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field without\"\n+                              \" Indexing — Indexed Name: %ud\", header_idx);\n+            } else {\n+                *pos = 64;\n+                pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx);\n+\n+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                              \"http2 hpack encode: Literal Header Field with \"\n+                              \"Incremental Indexing — Indexed Name: %ud\", header_idx);\n+            }\n+        }\n+\n+        pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+    }\n+\n+    return pos;\n+}\n+\n+\n+static ngx_int_t\n+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,\n+                        uint8_t *key, size_t key_len)\n+{\n+    ngx_http_v2_hpack_name_entry_t  *name;\n+    int                              i;\n+\n+    for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {\n+        name = &h2c->hpack_enc.heads[i];\n+\n+        if (name->hash_val == key_hash\n+            && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0)\n+        {\n+            if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) {\n+                return (h2c->hpack_enc.top - name->index) + 61;\n+            }\n+            break;\n+        }\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+\n+/* decide if a given header is present in the static dictionary, this could be\n+   done in several ways, but it seems the fastest one is \"exhaustive\" search */\n+static ngx_int_t\n+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len)\n+{\n+    /* the static dictionary of response only headers,\n+       although response headers can be put by origin,\n+       that would be rare */\n+    static const struct {\n+        u_char         len;\n+        const u_char   val[28];\n+        u_char         idx;\n+    } server_headers[] = {\n+        { 3, \"age\",                         21},//0\n+        { 3, \"via\",                         60},\n+        { 4, \"date\",                        33},//2\n+        { 4, \"etag\",                        34},\n+        { 4, \"link\",                        45},\n+        { 4, \"vary\",                        59},\n+        { 5, \"allow\",                       22},//6\n+        { 6, \"server\",                      54},//7\n+        { 7, \"expires\",                     36},//8\n+        { 7, \"refresh\",                     52},\n+        { 8, \"location\",                    46},//10\n+        {10, \"set-cookie\",                  55},//11\n+        {11, \"retry-after\",                 53},//12\n+        {12, \"content-type\",                31},//13\n+        {13, \"content-range\",               30},//14\n+        {13, \"accept-ranges\",               18},\n+        {13, \"cache-control\",               24},\n+        {13, \"last-modified\",               44},\n+        {14, \"content-length\",              28},//18\n+        {16, \"content-encoding\",            26},//19\n+        {16, \"content-language\",            27},\n+        {16, \"content-location\",            29},\n+        {16, \"www-authenticate\",            61},\n+        {17, \"transfer-encoding\",           57},//23\n+        {18, \"proxy-authenticate\",          48},//24\n+        {19, \"content-disposition\",         25},//25\n+        {25, \"strict-transport-security\",   56},//26\n+        {27, \"access-control-allow-origin\", 20},//27\n+        {99, \"\",                            99},\n+    }, *header;\n+\n+    /* for a given length, where to start the search\n+       since minimal length is 3, the table has a -3\n+       offset */\n+    static const int8_t start_at[] = {\n+        [3-3]  = 0,\n+        [4-3]  = 2,\n+        [5-3]  = 6,\n+        [6-3]  = 7,\n+        [7-3]  = 8,\n+        [8-3]  = 10,\n+        [9-3]  = -1,\n+        [10-3] = 11,\n+        [11-3] = 12,\n+        [12-3] = 13,\n+        [13-3] = 14,\n+        [14-3] = 18,\n+        [15-3] = -1,\n+        [16-3] = 19,\n+        [17-3] = 23,\n+        [18-3] = 24,\n+        [19-3] = 25,\n+        [20-3] = -1,\n+        [21-3] = -1,\n+        [22-3] = -1,\n+        [23-3] = -1,\n+        [24-3] = -1,\n+        [25-3] = 26,\n+        [26-3] = -1,\n+        [27-3] = 27,\n+    };\n+\n+    uint64_t pref;\n+    size_t   save_len = len, i;\n+    int8_t   start;\n+\n+    /* early exit for out of bounds lengths */\n+    if (len < 3 || len > 27) {\n+        return NGX_ERROR;\n+    }\n+\n+    start = start_at[len - 3];\n+    if (start == -1) {\n+        /* exit for non existent lengths */\n+        return NGX_ERROR;\n+    }\n+\n+    header = &server_headers[start_at[len - 3]];\n+\n+    /* load first 8 bytes of key, for fast comparison */\n+    if (len < 8) {\n+        pref = 0;\n+        if (len >= 4) {\n+            pref = *(uint32_t *)(val + len - 4) | 0x20202020;\n+            len -= 4;\n+        }\n+        while (len > 0) { /* 3 iterations at most */\n+            pref = (pref << 8) ^ (val[len - 1] | 0x20);\n+            len--;\n+        }\n+    } else {\n+        pref = *(uint64_t *)val | 0x2020202020202020;\n+        len -= 8;\n+    }\n+\n+    /* iterate over headers with the right length */\n+    while (header->len == save_len) {\n+        /* quickly compare the first 8 bytes, most tests will end here */\n+        if (pref != *(uint64_t *) header->val) {\n+            header++;\n+            continue;\n+        }\n+\n+        if (len == 0) {\n+            /* len == 0, indicates prefix held the entire key */\n+            return header->idx;\n+        }\n+        /* for longer keys compare the rest */\n+        i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */\n+\n+        while (i + 8 <= save_len) { /* 3 iterations at most */\n+            if ( *(uint64_t *)&header->val[i]\n+                 != (*(uint64_t *) &val[i]| 0x2020202020202020) )\n+            {\n+                header++;\n+                i = 0;\n+                break;\n+            }\n+            i += 8;\n+        }\n+\n+        if (i == 0) {\n+            continue;\n+        }\n+\n+        /* found the corresponding entry in the static dictionary */\n+        return header->idx;\n+    }\n+\n+    return NGX_ERROR;\n+}\n+\n+#else\n+\n+u_char *\n+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n+                         u_char *key, size_t key_len,\n+                         u_char *value, size_t value_len,\n+                         u_char *tmp)\n+{\n+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n+                   \"http2 output header: %*s: %*s\", key_len, key, value_len,\n+                   value);\n+\n+    *pos++ = 64;\n+    pos = ngx_http_v2_write_name(pos, key, key_len, tmp);\n+    pos = ngx_http_v2_write_value(pos, value, value_len, tmp);\n+\n+    return pos;\n+}\n+\n+#endif\ndiff -uNr a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c\n--- a/src/http/v3/ngx_http_v3.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -0,0 +1,2092 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+\n+typedef struct {\n+    ngx_str_t           name;\n+    ngx_uint_t          offset;\n+    ngx_uint_t          hash;\n+    ngx_http_header_t  *hh;\n+} ngx_http_v3_parse_header_t;\n+\n+\n+/* errors */\n+#define NGX_HTTP_V3_NO_ERROR                     0x0\n+#define NGX_HTTP_V3_INTERNAL_ERROR               0x3\n+\n+\n+static void ngx_http_v3_handler(ngx_connection_t *c);\n+\n+static ngx_http_v3_stream_t *ngx_http_v3_stream_lookup(\n+    ngx_http_v3_connection_t *h3c, ngx_uint_t stream_id);\n+static ngx_http_v3_stream_t *ngx_http_v3_create_stream(\n+    ngx_http_v3_connection_t *h3c);\n+static void ngx_http_v3_close_stream_handler(ngx_event_t *ev);\n+\n+static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r,\n+    ngx_http_v3_header_t *header);\n+static ngx_int_t ngx_http_v3_pseudo_header(ngx_http_request_t *r,\n+    ngx_http_v3_header_t *header);\n+static ngx_int_t ngx_http_v3_parse_path(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_method(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_scheme(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_authority(ngx_http_request_t *r,\n+    ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r,\n+    ngx_http_v3_parse_header_t *header, ngx_str_t *value);\n+static ngx_int_t ngx_http_v3_cookie(ngx_http_request_t *r,\n+    ngx_http_v3_header_t *header);\n+static ngx_int_t ngx_http_v3_construct_cookie_header(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_v3_construct_request_line(ngx_http_request_t *r);\n+\n+static void ngx_http_v3_run_request(ngx_http_request_t *r);\n+static ngx_int_t ngx_http_v3_process_request_body(ngx_http_request_t *r,\n+    ngx_uint_t do_read, ngx_uint_t last);\n+static ngx_int_t ngx_http_v3_filter_request_body(ngx_http_request_t *r);\n+static void ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r);\n+\n+static ngx_chain_t *ngx_http_v3_send_chain(ngx_connection_t *fc,\n+    ngx_chain_t *in, off_t limit);\n+\n+static void ngx_http_v3_finalize_connection(ngx_http_v3_connection_t *h3c,\n+    ngx_uint_t status);\n+\n+static void ngx_http_v3_pool_cleanup(void *data);\n+\n+\n+static ngx_http_v3_parse_header_t  ngx_http_v3_parse_headers[] = {\n+    { ngx_string(\"host\"),\n+      offsetof(ngx_http_headers_in_t, host), 0, NULL },\n+\n+    { ngx_string(\"accept-encoding\"),\n+      offsetof(ngx_http_headers_in_t, accept_encoding), 0, NULL },\n+\n+    { ngx_string(\"accept-language\"),\n+      offsetof(ngx_http_headers_in_t, accept_language), 0, NULL },\n+\n+    { ngx_string(\"user-agent\"),\n+      offsetof(ngx_http_headers_in_t, user_agent), 0, NULL },\n+\n+    { ngx_null_string, 0, 0, NULL }\n+};\n+\n+\n+void\n+ngx_http_v3_init(ngx_event_t *rev)\n+{\n+    ngx_connection_t          *c;\n+    ngx_pool_cleanup_t        *cln;\n+    ngx_http_connection_t     *hc;\n+    ngx_http_v3_srv_conf_t    *h3scf;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    c = rev->data;\n+    hc = c->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"init http3 connection\");\n+\n+    c->log->action = \"processing HTTP/3 connection\";\n+\n+    h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t));\n+    if (h3c == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);\n+\n+    h3c->h3 = quiche_h3_conn_new_with_transport(c->quic->conn, h3scf->http3);\n+    if (h3c->h3 == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    h3c->http_connection = hc;\n+\n+    h3c->connection = c;\n+\n+    h3c->pool = c->pool;\n+\n+    c->data = h3c;\n+\n+    c->quic->handler = ngx_http_v3_handler;\n+\n+    cln = ngx_pool_cleanup_add(c->pool, 0);\n+    if (cln == NULL) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    cln->handler = ngx_http_v3_pool_cleanup;\n+    cln->data = h3c;\n+\n+    ngx_rbtree_init(&h3c->streams, &h3c->streams_sentinel,\n+                    ngx_rbtree_insert_value);\n+}\n+\n+\n+static int\n+ngx_http_v3_for_each_header(uint8_t *name, size_t name_len,\n+    uint8_t *value, size_t value_len, void *argp)\n+{\n+    ngx_int_t                   rc;\n+    ngx_table_elt_t            *h;\n+    ngx_http_header_t          *hh;\n+    ngx_http_request_t         *r;\n+    ngx_http_v3_header_t        header;\n+    ngx_http_core_srv_conf_t   *cscf;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    static ngx_str_t cookie = ngx_string(\"cookie\");\n+\n+    r = argp;\n+\n+    /* Duplicate the header name because we don't own it. */\n+    header.name.data = ngx_pnalloc(r->pool, name_len);\n+    if (header.name.data == NULL) {\n+        return NGX_ERROR;\n+    }\n+    header.name.len = name_len;\n+\n+    ngx_memcpy(header.name.data, name, name_len);\n+\n+    /* Duplicate the header value because we don't own it. Some of the\n+     * functions that process headers require a NULL-terminated string,\n+     * so allocate enough memory for that. */\n+    header.value.data = ngx_pcalloc(r->pool, value_len + 1);\n+    if (header.value.data == NULL) {\n+        return NGX_ERROR;\n+    }\n+    header.value.len = value_len;\n+\n+    ngx_memcpy(header.value.data, value, value_len);\n+\n+    if (ngx_http_v3_validate_header(r, &header) != NGX_OK) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* Check for pseudo-header. */\n+    if (header.name.data[0] == ':') {\n+        rc = ngx_http_v3_pseudo_header(r, &header);\n+\n+        if (rc == NGX_OK) {\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                           \"http3 header: \\\":%V: %V\\\"\",\n+                           &header.name, &header.value);\n+\n+            return NGX_OK;\n+        }\n+\n+        return NGX_ERROR;\n+    }\n+\n+    if (r->invalid_header) {\n+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+        if (cscf->ignore_invalid_headers) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid header: \\\"%V\\\"\", &header.name);\n+\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    /* Handle Cookie header separately. Not sure why, but the HTTP/2 code does\n+     * the same. */\n+    if (header.name.len == cookie.len\n+        && ngx_memcmp(header.name.data, cookie.data, cookie.len) == 0)\n+    {\n+        if (ngx_http_v3_cookie(r, &header) != NGX_OK) {\n+            return NGX_ERROR;\n+        }\n+\n+    } else {\n+        h = ngx_list_push(&r->headers_in.headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->key.len = header.name.len;\n+        h->key.data = header.name.data;\n+\n+        /*\n+         * TODO Optimization: precalculate hash\n+         * and handler for indexed headers.\n+         */\n+        h->hash = ngx_hash_key(h->key.data, h->key.len);\n+\n+        h->value.len = header.value.len;\n+        h->value.data = header.value.data;\n+\n+        h->lowcase_key = h->key.data;\n+\n+        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+        hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n+                           h->lowcase_key, h->key.len);\n+\n+        if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 header: \\\"%V: %V\\\"\",\n+                   &header.name, &header.value);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_process_headers(ngx_connection_t *c, quiche_h3_event *ev,\n+    int64_t stream_id)\n+{\n+    int                        rc;\n+    ngx_http_v3_stream_t      *stream;\n+    ngx_http_v3_srv_conf_t    *h3scf;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 process headers\");\n+\n+    h3c = c->data;\n+\n+    h3scf = ngx_http_get_module_srv_conf(h3c->http_connection->conf_ctx,\n+                                         ngx_http_v3_module);\n+\n+    if (h3c->connection->requests >= h3scf->max_requests) {\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_NO_ERROR);\n+        return;\n+    }\n+\n+    /* Create a new stream to handle the incoming request. */\n+    stream = ngx_http_v3_create_stream(h3c);\n+    if (stream == NULL) {\n+        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to create HTTP/3 stream\");\n+\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    stream->id = stream_id;\n+\n+    stream->node.key = stream_id;\n+\n+    ngx_rbtree_insert(&h3c->streams, &stream->node);\n+\n+    /* Populate ngx_http_request_t from raw HTTP/3 headers. */\n+    rc = quiche_h3_event_for_each_header(ev,\n+        ngx_http_v3_for_each_header, stream->request);\n+\n+    if (rc != NGX_OK) {\n+        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n+                      \"received invalid HTTP/3 headers\");\n+\n+        ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);\n+        return;\n+    }\n+\n+    stream->in_closed = !quiche_h3_event_headers_has_body(ev);\n+\n+    ngx_http_v3_run_request(stream->request);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_process_data(ngx_connection_t *c, int64_t stream_id)\n+{\n+    int                        rc;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_stream_t      *stream;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 process data\");\n+\n+    h3c = c->data;\n+\n+    stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+    if (stream == NULL) {\n+\n+        return NGX_OK;\n+    }\n+\n+    if (stream->skip_data) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                       \"skipping http3 DATA frame\");\n+\n+        return NGX_OK;\n+    }\n+\n+    r = stream->request;\n+\n+    if (!r->request_body) {\n+        return NGX_AGAIN;\n+    }\n+\n+    rc = ngx_http_v3_process_request_body(r, 1, stream->in_closed);\n+\n+    if (rc == NGX_AGAIN) {\n+        return NGX_AGAIN;\n+    }\n+\n+    if (rc != NGX_OK) {\n+        stream->skip_data = 1;\n+        ngx_http_finalize_request(r, rc);\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_process_blocked_streams(ngx_http_v3_connection_t *h3c)\n+{\n+    ngx_event_t               *wev;\n+    quiche_stream_iter        *writable;\n+    ngx_http_v3_stream_t      *stream;\n+    uint64_t                   stream_id;\n+\n+    writable = quiche_conn_writable(h3c->connection->quic->conn);\n+\n+    while (quiche_stream_iter_next(writable, &stream_id)) {\n+        stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+        if (stream == NULL) {\n+            continue;\n+        }\n+\n+        if (!stream->blocked) {\n+            continue;\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                       \"http3 stream unblocked %ui\", stream->id);\n+\n+        stream->blocked = 0;\n+\n+        wev = stream->request->connection->write;\n+\n+        wev->active = 0;\n+        wev->ready = 1;\n+\n+        if (!wev->delayed) {\n+            wev->handler(wev);\n+        }\n+    }\n+\n+    quiche_stream_iter_free(writable);\n+}\n+\n+\n+static void\n+ngx_http_v3_handler(ngx_connection_t *c)\n+{\n+    ngx_http_v3_connection_t  *h3c;\n+    ngx_http_v3_stream_t      *stream;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 handler\");\n+\n+    h3c = c->data;\n+\n+    if (c->error) {\n+        ngx_http_v3_finalize_connection(h3c, 0);\n+        return;\n+    }\n+\n+    ngx_http_v3_process_blocked_streams(h3c);\n+\n+    while (!c->error) {\n+        quiche_h3_event  *ev;\n+\n+        int64_t stream_id = quiche_h3_conn_poll(h3c->h3, c->quic->conn, &ev);\n+        if (stream_id < 0) {\n+            break;\n+        }\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                       \"http3 event stream:%ui ev:%ui\", stream_id,\n+                       quiche_h3_event_type(ev));\n+\n+        switch (quiche_h3_event_type(ev)) {\n+            case QUICHE_H3_EVENT_HEADERS: {\n+                ngx_http_v3_process_headers(c, ev, stream_id);\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_DATA: {\n+                if (ngx_http_v3_process_data(c, stream_id) == NGX_AGAIN) {\n+                    quiche_h3_event_free(ev);\n+                    return;\n+                }\n+\n+                break;\n+            }\n+\n+            case QUICHE_H3_EVENT_FINISHED: {\n+                /* Lookup stream. If there isn't one, it means it has already\n+                 * been closed, so ignore the event. */\n+                stream = ngx_http_v3_stream_lookup(h3c, stream_id);\n+\n+                if (stream != NULL && !stream->in_closed) {\n+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n+                                   \"http3 finished\");\n+\n+                    stream->in_closed = 1;\n+\n+                    /* Flush request body that was buffered. */\n+                    if (stream->request->request_body) {\n+                        ngx_http_v3_process_request_body(stream->request, 0, 1);\n+                    }\n+                }\n+\n+                break;\n+            }\n+        }\n+\n+        quiche_h3_event_free(ev);\n+    }\n+}\n+\n+\n+static ngx_http_v3_stream_t *\n+ngx_http_v3_create_stream(ngx_http_v3_connection_t *h3c)\n+{\n+    ngx_log_t                 *log;\n+    ngx_event_t               *rev, *wev;\n+    ngx_connection_t          *fc;\n+    ngx_http_log_ctx_t        *ctx;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_stream_t      *stream;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 create stream\");\n+\n+    fc = h3c->free_fake_connections;\n+\n+    if (fc) {\n+        h3c->free_fake_connections = fc->data;\n+\n+        rev = fc->read;\n+        wev = fc->write;\n+        log = fc->log;\n+        ctx = log->data;\n+\n+    } else {\n+        fc = ngx_palloc(h3c->pool, sizeof(ngx_connection_t));\n+        if (fc == NULL) {\n+            return NULL;\n+        }\n+\n+        rev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));\n+        if (rev == NULL) {\n+            return NULL;\n+        }\n+\n+        wev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));\n+        if (wev == NULL) {\n+            return NULL;\n+        }\n+\n+        log = ngx_palloc(h3c->pool, sizeof(ngx_log_t));\n+        if (log == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx = ngx_palloc(h3c->pool, sizeof(ngx_http_log_ctx_t));\n+        if (ctx == NULL) {\n+            return NULL;\n+        }\n+\n+        ctx->connection = fc;\n+        ctx->request = NULL;\n+        ctx->current_request = NULL;\n+    }\n+\n+    ngx_memcpy(log, h3c->connection->log, sizeof(ngx_log_t));\n+\n+    log->data = ctx;\n+\n+    ngx_memzero(rev, sizeof(ngx_event_t));\n+\n+    rev->data = fc;\n+    rev->ready = 1;\n+    rev->handler = ngx_http_v3_close_stream_handler;\n+    rev->log = log;\n+\n+    ngx_memcpy(wev, rev, sizeof(ngx_event_t));\n+\n+    wev->write = 1;\n+\n+    ngx_memcpy(fc, h3c->connection, sizeof(ngx_connection_t));\n+\n+    fc->data = h3c->http_connection;\n+    fc->quic = h3c->connection->quic;\n+    fc->read = rev;\n+    fc->write = wev;\n+    fc->sent = 0;\n+    fc->buffer = NULL;\n+    fc->log = log;\n+    fc->buffered = 0;\n+    fc->sndlowat = 1;\n+    fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n+\n+    fc->send_chain = ngx_http_v3_send_chain;\n+    fc->need_last_buf = 1;\n+\n+    r = ngx_http_create_request(fc);\n+    if (r == NULL) {\n+        return NULL;\n+    }\n+\n+    ngx_str_set(&r->http_protocol, \"HTTP/3\");\n+\n+    r->http_version = NGX_HTTP_VERSION_3;\n+    r->valid_location = 1;\n+\n+    fc->data = r;\n+    h3c->connection->requests++;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    r->header_in = ngx_create_temp_buf(r->pool,\n+                                       cscf->client_header_buffer_size);\n+    if (r->header_in == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    if (ngx_list_init(&r->headers_in.headers, r->pool, 20,\n+                      sizeof(ngx_table_elt_t))\n+        != NGX_OK)\n+    {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n+\n+    stream = ngx_pcalloc(h3c->pool, sizeof(ngx_http_v3_stream_t));\n+    if (stream == NULL) {\n+        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NULL;\n+    }\n+\n+    r->qstream = stream;\n+\n+    stream->request = r;\n+    stream->connection = h3c;\n+\n+    h3c->processing++;\n+\n+    return stream;\n+}\n+\n+\n+static ngx_http_v3_stream_t *\n+ngx_http_v3_stream_lookup(ngx_http_v3_connection_t *h3c, ngx_uint_t stream_id)\n+{\n+    ngx_rbtree_node_t  *node, *sentinel;\n+\n+    node = h3c->streams.root;\n+    sentinel = h3c->streams.sentinel;\n+\n+    while (node != sentinel) {\n+\n+        if (stream_id < node->key) {\n+            node = node->left;\n+            continue;\n+        }\n+\n+        if (stream_id > node->key) {\n+            node = node->right;\n+            continue;\n+        }\n+\n+        /* stream_id == node->key */\n+\n+        return (ngx_http_v3_stream_t *) node;\n+    }\n+\n+    /* not found */\n+\n+    return NULL;\n+}\n+\n+\n+/* The following functions are copied from the HTTP/2 module, and adapted to\n+ * work independently. In theory we could refactor the HTTP/2 module to expose\n+ * these functions, but that would be fairly invasive and likely cause more\n+ * merge conflicts in the future. */\n+\n+\n+static ngx_int_t\n+ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n+{\n+    u_char                     ch;\n+    ngx_uint_t                 i;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    if (header->name.len == 0) {\n+        return NGX_ERROR;\n+    }\n+\n+    r->invalid_header = 0;\n+\n+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+\n+    for (i = (header->name.data[0] == ':'); i != header->name.len; i++) {\n+        ch = header->name.data[i];\n+\n+        if ((ch >= 'a' && ch <= 'z')\n+            || (ch == '-')\n+            || (ch >= '0' && ch <= '9')\n+            || (ch == '_' && cscf->underscores_in_headers))\n+        {\n+            continue;\n+        }\n+\n+        if (ch == '\\0' || ch == LF || ch == CR || ch == ':'\n+            || (ch >= 'A' && ch <= 'Z'))\n+        {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid header name: \\\"%V\\\"\",\n+                          &header->name);\n+\n+            return NGX_ERROR;\n+        }\n+\n+        r->invalid_header = 1;\n+    }\n+\n+    for (i = 0; i != header->value.len; i++) {\n+        ch = header->value.data[i];\n+\n+        if (ch == '\\0' || ch == LF || ch == CR) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent header \\\"%V\\\" with \"\n+                          \"invalid value: \\\"%V\\\"\",\n+                          &header->name, &header->value);\n+\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_pseudo_header(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n+{\n+    header->name.len--;\n+    header->name.data++;\n+\n+    switch (header->name.len) {\n+    case 4:\n+        if (ngx_memcmp(header->name.data, \"path\", sizeof(\"path\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_path(r, &header->value);\n+        }\n+\n+        break;\n+\n+    case 6:\n+        if (ngx_memcmp(header->name.data, \"method\", sizeof(\"method\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_method(r, &header->value);\n+        }\n+\n+        if (ngx_memcmp(header->name.data, \"scheme\", sizeof(\"scheme\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_scheme(r, &header->value);\n+        }\n+\n+        break;\n+\n+    case 9:\n+        if (ngx_memcmp(header->name.data, \"authority\", sizeof(\"authority\") - 1)\n+            == 0)\n+        {\n+            return ngx_http_v3_parse_authority(r, &header->value);\n+        }\n+\n+        break;\n+    }\n+\n+    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                  \"client sent unknown pseudo-header \\\":%V\\\"\",\n+                  &header->name);\n+\n+    return NGX_DECLINED;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_path(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    if (r->unparsed_uri.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :path header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (value->len == 0) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent empty :path header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    r->uri_start = value->data;\n+    r->uri_end = value->data + value->len;\n+\n+    if (ngx_http_parse_uri(r) != NGX_OK) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent invalid :path header: \\\"%V\\\"\", value);\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (ngx_http_process_request_uri(r) != NGX_OK) {\n+        /*\n+         * request has been finalized already\n+         * in ngx_http_process_request_uri()\n+         */\n+        return NGX_ABORT;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_method(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    size_t         k, len;\n+    ngx_uint_t     n;\n+    const u_char  *p, *m;\n+\n+    /*\n+     * This array takes less than 256 sequential bytes,\n+     * and if typical CPU cache line size is 64 bytes,\n+     * it is prefetched for 4 load operations.\n+     */\n+    static const struct {\n+        u_char            len;\n+        const u_char      method[11];\n+        uint32_t          value;\n+    } tests[] = {\n+        { 3, \"GET\",       NGX_HTTP_GET },\n+        { 4, \"POST\",      NGX_HTTP_POST },\n+        { 4, \"HEAD\",      NGX_HTTP_HEAD },\n+        { 7, \"OPTIONS\",   NGX_HTTP_OPTIONS },\n+        { 8, \"PROPFIND\",  NGX_HTTP_PROPFIND },\n+        { 3, \"PUT\",       NGX_HTTP_PUT },\n+        { 5, \"MKCOL\",     NGX_HTTP_MKCOL },\n+        { 6, \"DELETE\",    NGX_HTTP_DELETE },\n+        { 4, \"COPY\",      NGX_HTTP_COPY },\n+        { 4, \"MOVE\",      NGX_HTTP_MOVE },\n+        { 9, \"PROPPATCH\", NGX_HTTP_PROPPATCH },\n+        { 4, \"LOCK\",      NGX_HTTP_LOCK },\n+        { 6, \"UNLOCK\",    NGX_HTTP_UNLOCK },\n+        { 5, \"PATCH\",     NGX_HTTP_PATCH },\n+        { 5, \"TRACE\",     NGX_HTTP_TRACE }\n+    }, *test;\n+\n+    if (r->method_name.len) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :method header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (value->len == 0) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent empty :method header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    r->method_name.len = value->len;\n+    r->method_name.data = value->data;\n+\n+    len = r->method_name.len;\n+    n = sizeof(tests) / sizeof(tests[0]);\n+    test = tests;\n+\n+    do {\n+        if (len == test->len) {\n+            p = r->method_name.data;\n+            m = test->method;\n+            k = len;\n+\n+            do {\n+                if (*p++ != *m++) {\n+                    goto next;\n+                }\n+            } while (--k);\n+\n+            r->method = test->value;\n+            return NGX_OK;\n+        }\n+\n+    next:\n+        test++;\n+\n+    } while (--n);\n+\n+    p = r->method_name.data;\n+\n+    do {\n+        if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent invalid method: \\\"%V\\\"\",\n+                          &r->method_name);\n+\n+            return NGX_DECLINED;\n+        }\n+\n+        p++;\n+\n+    } while (--len);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    if (r->schema_start) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent duplicate :scheme header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    if (value->len == 0) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client sent empty :scheme header\");\n+\n+        return NGX_DECLINED;\n+    }\n+\n+    r->schema_start = value->data;\n+    r->schema_end = value->data + value->len;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_authority(ngx_http_request_t *r, ngx_str_t *value)\n+{\n+    return ngx_http_v3_parse_header(r, &ngx_http_v3_parse_headers[0], value);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_parse_header(ngx_http_request_t *r,\n+    ngx_http_v3_parse_header_t *header, ngx_str_t *value)\n+{\n+    ngx_table_elt_t            *h;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    h->key.len = header->name.len;\n+    h->key.data = header->name.data;\n+    h->lowcase_key = header->name.data;\n+\n+    if (header->hh == NULL) {\n+        header->hash = ngx_hash_key(header->name.data, header->name.len);\n+\n+        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+        header->hh = ngx_hash_find(&cmcf->headers_in_hash, header->hash,\n+                                   h->lowcase_key, h->key.len);\n+        if (header->hh == NULL) {\n+            return NGX_ERROR;\n+        }\n+    }\n+\n+    h->hash = header->hash;\n+\n+    h->value.len = value->len;\n+    h->value.data = value->data;\n+\n+    if (header->hh->handler(r, h, header->hh->offset) != NGX_OK) {\n+        /* header handler has already finalized request */\n+        return NGX_ABORT;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_construct_request_line(ngx_http_request_t *r)\n+{\n+    u_char  *p;\n+\n+    static const u_char ending[] = \" HTTP/3\";\n+\n+    if (r->method_name.len == 0\n+        || r->schema_start == NULL\n+        || r->unparsed_uri.len == 0)\n+    {\n+        if (r->method_name.len == 0) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent no :method header\");\n+\n+        } else if (r->schema_start == NULL) {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent no :scheme header\");\n+\n+        } else {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client sent no :path header\");\n+        }\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.len = r->method_name.len + 1\n+                          + r->unparsed_uri.len\n+                          + sizeof(ending) - 1;\n+\n+    p = ngx_pnalloc(r->pool, r->request_line.len + 1);\n+    if (p == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    r->request_line.data = p;\n+\n+    p = ngx_cpymem(p, r->method_name.data, r->method_name.len);\n+\n+    *p++ = ' ';\n+\n+    p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);\n+\n+    ngx_memcpy(p, ending, sizeof(ending));\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 request line: \\\"%V\\\"\", &r->request_line);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_cookie(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n+{\n+    ngx_str_t    *val;\n+    ngx_array_t  *cookies;\n+\n+    cookies = r->qstream->cookies;\n+\n+    if (cookies == NULL) {\n+        cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t));\n+        if (cookies == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        r->qstream->cookies = cookies;\n+    }\n+\n+    val = ngx_array_push(cookies);\n+    if (val == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    val->len = header->value.len;\n+    val->data = header->value.data;\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_construct_cookie_header(ngx_http_request_t *r)\n+{\n+    u_char                     *buf, *p, *end;\n+    size_t                      len;\n+    ngx_str_t                  *vals;\n+    ngx_uint_t                  i;\n+    ngx_array_t                *cookies;\n+    ngx_table_elt_t            *h;\n+    ngx_http_header_t          *hh;\n+    ngx_http_core_main_conf_t  *cmcf;\n+\n+    static ngx_str_t cookie = ngx_string(\"cookie\");\n+\n+    cookies = r->qstream->cookies;\n+\n+    if (cookies == NULL) {\n+        return NGX_OK;\n+    }\n+\n+    vals = cookies->elts;\n+\n+    i = 0;\n+    len = 0;\n+\n+    do {\n+        len += vals[i].len + 2;\n+    } while (++i != cookies->nelts);\n+\n+    len -= 2;\n+\n+    buf = ngx_pnalloc(r->pool, len + 1);\n+    if (buf == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    p = buf;\n+    end = buf + len;\n+\n+    for (i = 0; /* void */ ; i++) {\n+\n+        p = ngx_cpymem(p, vals[i].data, vals[i].len);\n+\n+        if (p == end) {\n+            *p = '\\0';\n+            break;\n+        }\n+\n+        *p++ = ';'; *p++ = ' ';\n+    }\n+\n+    h = ngx_list_push(&r->headers_in.headers);\n+    if (h == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(\n+                                    ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e');\n+\n+    h->key.len = cookie.len;\n+    h->key.data = cookie.data;\n+\n+    h->value.len = len;\n+    h->value.data = buf;\n+\n+    h->lowcase_key = cookie.data;\n+\n+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n+\n+    hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n+                       h->lowcase_key, h->key.len);\n+\n+    if (hh == NULL) {\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n+        return NGX_ERROR;\n+    }\n+\n+    if (hh->handler(r, h, hh->offset) != NGX_OK) {\n+        /*\n+         * request has been finalized already\n+         * in ngx_http_process_multi_header_lines()\n+         */\n+        return NGX_ERROR;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_run_request(ngx_http_request_t *r)\n+{\n+    if (ngx_http_v3_construct_request_line(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    if (ngx_http_v3_construct_cookie_header(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;\n+\n+    if (ngx_http_process_request_header(r) != NGX_OK) {\n+        return;\n+    }\n+\n+    if (r->headers_in.content_length_n > 0 && r->qstream->in_closed) {\n+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                      \"client prematurely closed stream\");\n+\n+        r->qstream->skip_data = 1;\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n+        return;\n+    }\n+\n+    if (r->headers_in.content_length_n == -1 && !r->qstream->in_closed) {\n+        r->headers_in.chunked = 1;\n+    }\n+\n+    ngx_http_process_request(r);\n+}\n+\n+\n+ngx_int_t\n+ngx_http_v3_read_request_body(ngx_http_request_t *r)\n+{\n+    off_t                      len;\n+    ngx_http_v3_stream_t      *stream;\n+    ngx_http_request_body_t   *rb;\n+    ngx_http_core_loc_conf_t  *clcf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 read request body\");\n+\n+    stream = r->qstream;\n+    rb = r->request_body;\n+\n+    if (stream->skip_data) {\n+        r->request_body_no_buffering = 0;\n+        rb->post_handler(r);\n+        return NGX_OK;\n+    }\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    len = r->headers_in.content_length_n;\n+\n+    if (r->request_body_no_buffering && !stream->in_closed) {\n+\n+        if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {\n+            len = clcf->client_body_buffer_size;\n+        }\n+\n+        rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);\n+\n+    } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size\n+               && !r->request_body_in_file_only)\n+    {\n+        rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);\n+\n+    } else {\n+        rb->buf = ngx_calloc_buf(r->pool);\n+\n+        if (rb->buf != NULL) {\n+            rb->buf->sync = 1;\n+        }\n+    }\n+\n+    if (rb->buf == NULL) {\n+        stream->skip_data = 1;\n+        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n+    }\n+\n+    rb->rest = 1;\n+\n+    if (stream->in_closed) {\n+        r->request_body_no_buffering = 0;\n+\n+        return ngx_http_v3_process_request_body(r, 0, 1);\n+    }\n+\n+    /* TODO: set timer */\n+    ngx_add_timer(r->connection->read, clcf->client_body_timeout);\n+\n+    r->read_event_handler = ngx_http_v3_read_client_request_body_handler;\n+    r->write_event_handler = ngx_http_request_empty_handler;\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_process_request_body(ngx_http_request_t *r, ngx_uint_t do_read,\n+    ngx_uint_t last)\n+{\n+    ssize_t                    len = 0;\n+    ngx_buf_t                 *buf;\n+    ngx_int_t                  rc;\n+    ngx_connection_t          *c, *fc;\n+    ngx_http_v3_connection_t  *h3c;\n+    ngx_http_request_body_t   *rb;\n+    ngx_http_core_loc_conf_t  *clcf;\n+\n+    fc = r->connection;\n+    h3c = r->qstream->connection;\n+    c = h3c->connection;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 process request body\");\n+\n+    rb = r->request_body;\n+    buf = rb->buf;\n+\n+    if (buf->sync) {\n+        buf->pos = buf->start;\n+        buf->last = buf->start;\n+\n+        r->request_body_in_file_only = 1;\n+    }\n+\n+    if (do_read) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"http3 reading %z bytes of request body\",\n+                       buf->end - buf->last);\n+\n+        if (buf->last == buf->end) {\n+            return NGX_AGAIN;\n+        }\n+\n+        len = quiche_h3_recv_body(h3c->h3, c->quic->conn, r->qstream->id,\n+                                  buf->last, buf->end - buf->last);\n+\n+        if (len == QUICHE_ERR_DONE) {\n+            return NGX_AGAIN;\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                       \"http3 read %z bytes of request body\", len);\n+\n+        buf->last += len;\n+    }\n+\n+    if (last) {\n+        rb->rest = 0;\n+\n+        if (fc->read->timer_set) {\n+            ngx_del_timer(fc->read);\n+        }\n+\n+        if (r->request_body_no_buffering) {\n+            ngx_post_event(fc->read, &ngx_posted_events);\n+            return NGX_OK;\n+        }\n+\n+        rc = ngx_http_v3_filter_request_body(r);\n+\n+        if (rc != NGX_OK) {\n+            return rc;\n+        }\n+\n+        if (buf->sync) {\n+            /* prevent reusing this buffer in the upstream module */\n+            rb->buf = NULL;\n+        }\n+\n+        if (r->headers_in.chunked) {\n+            r->headers_in.content_length_n = rb->received;\n+        }\n+\n+        r->read_event_handler = ngx_http_block_reading;\n+        rb->post_handler(r);\n+\n+        return NGX_OK;\n+    }\n+\n+    if (len == 0) {\n+        return NGX_OK;\n+    }\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+    ngx_add_timer(fc->read, clcf->client_body_timeout);\n+\n+    if (r->request_body_no_buffering) {\n+        ngx_post_event(fc->read, &ngx_posted_events);\n+        return NGX_AGAIN;\n+    }\n+\n+    if (buf->sync) {\n+        return ngx_http_v3_filter_request_body(r);\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_filter_request_body(ngx_http_request_t *r)\n+{\n+    ngx_buf_t                 *b, *buf;\n+    ngx_int_t                  rc;\n+    ngx_chain_t               *cl;\n+    ngx_http_request_body_t   *rb;\n+    ngx_http_core_loc_conf_t  *clcf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 filter request body\");\n+\n+    rb = r->request_body;\n+    buf = rb->buf;\n+\n+    if (buf->pos == buf->last && rb->rest) {\n+        cl = NULL;\n+        goto update;\n+    }\n+\n+    cl = ngx_chain_get_free_buf(r->pool, &rb->free);\n+    if (cl == NULL) {\n+        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n+    }\n+\n+    b = cl->buf;\n+\n+    ngx_memzero(b, sizeof(ngx_buf_t));\n+\n+    if (buf->pos != buf->last) {\n+        r->request_length += buf->last - buf->pos;\n+        rb->received += buf->last - buf->pos;\n+\n+        if (r->headers_in.content_length_n != -1) {\n+            if (rb->received > r->headers_in.content_length_n) {\n+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                              \"client intended to send body data \"\n+                              \"larger than declared\");\n+\n+                return NGX_HTTP_BAD_REQUEST;\n+            }\n+\n+        } else {\n+            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+            if (clcf->client_max_body_size\n+                && rb->received > clcf->client_max_body_size)\n+            {\n+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n+                              \"client intended to send too large chunked body: \"\n+                              \"%O bytes\", rb->received);\n+\n+                return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;\n+            }\n+        }\n+\n+        b->temporary = 1;\n+        b->pos = buf->pos;\n+        b->last = buf->last;\n+        b->start = b->pos;\n+        b->end = b->last;\n+\n+        buf->pos = buf->last;\n+    }\n+\n+    if (!rb->rest) {\n+        if (r->headers_in.content_length_n != -1\n+            && r->headers_in.content_length_n != rb->received)\n+        {\n+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n+                          \"client prematurely closed stream: \"\n+                          \"only %O out of %O bytes of request body received\",\n+                          rb->received, r->headers_in.content_length_n);\n+\n+            return NGX_HTTP_BAD_REQUEST;\n+        }\n+\n+        b->last_buf = 1;\n+    }\n+\n+    b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_request_body;\n+    b->flush = r->request_body_no_buffering;\n+\n+update:\n+\n+    rc = ngx_http_top_request_body_filter(r, cl);\n+\n+    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl,\n+                            (ngx_buf_tag_t) &ngx_http_v3_filter_request_body);\n+\n+    return rc;\n+}\n+\n+\n+static void\n+ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r)\n+{\n+    ngx_connection_t  *fc;\n+\n+    fc = r->connection;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                   \"http3 read client request body handler\");\n+\n+    if (fc->read->timedout) {\n+        ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, \"client timed out\");\n+\n+        fc->timedout = 1;\n+        r->qstream->skip_data = 1;\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);\n+        return;\n+    }\n+\n+    if (fc->error) {\n+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n+                      \"client prematurely closed stream\");\n+\n+        r->qstream->skip_data = 1;\n+\n+        ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n+        return;\n+    }\n+}\n+\n+\n+ngx_int_t\n+ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r)\n+{\n+    ngx_buf_t                 *buf;\n+    ngx_int_t                  rc;\n+    ngx_connection_t          *fc;\n+    ngx_http_v3_stream_t      *stream;\n+\n+    stream = r->qstream;\n+    fc = r->connection;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                   \"http3 read unbuffered request body\");\n+\n+    if (fc->read->timedout) {\n+        stream->skip_data = 1;\n+        fc->timedout = 1;\n+\n+        return NGX_HTTP_REQUEST_TIME_OUT;\n+    }\n+\n+    if (fc->error) {\n+        stream->skip_data = 1;\n+        return NGX_HTTP_BAD_REQUEST;\n+    }\n+\n+    rc = ngx_http_v3_filter_request_body(r);\n+\n+    if (rc != NGX_OK) {\n+        stream->skip_data = 1;\n+        return rc;\n+    }\n+\n+    if (!r->request_body->rest) {\n+        return NGX_OK;\n+    }\n+\n+    if (r->request_body->busy != NULL) {\n+        return NGX_AGAIN;\n+    }\n+\n+    buf = r->request_body->buf;\n+\n+    buf->pos = buf->start;\n+    buf->last = buf->start;\n+\n+    ngx_post_event(stream->connection->connection->read, &ngx_posted_events);\n+\n+    return NGX_AGAIN;\n+}\n+\n+\n+/* End of functions copied from HTTP/2 module. */\n+\n+\n+ngx_int_t\n+ngx_http_v3_send_response(ngx_http_request_t *r)\n+{\n+    u_char                    *tmp;\n+    u_char                     status[3], content_len[NGX_OFF_T_LEN],\n+                               last_modified[sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1],\n+                               addr[NGX_SOCKADDR_STRLEN];\n+    size_t                     len;\n+    ngx_array_t               *headers;\n+    ngx_str_t                  host, location;\n+    ngx_uint_t                 i, port, fin;\n+    ngx_list_part_t           *part;\n+    ngx_table_elt_t           *header;\n+    ngx_connection_t          *c, *fc;\n+    quiche_h3_header          *h;\n+    ngx_http_v3_connection_t  *h3c;\n+    ngx_http_core_loc_conf_t  *clcf;\n+    ngx_http_core_srv_conf_t  *cscf;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n+                   \"http3 send response\");\n+\n+    fc = r->connection;\n+\n+    h3c = r->qstream->connection;\n+    c = h3c->connection;\n+\n+    headers = ngx_array_create(r->pool, 1, sizeof(quiche_h3_header));\n+    if (headers == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    /* Generate :status pseudo-header. */\n+    {\n+        h = ngx_array_push(headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \":status\";\n+        h->name_len = sizeof(\":status\") - 1;\n+\n+        h->value = status;\n+        h->value_len =\n+            ngx_sprintf(status, \"%03ui\", r->headers_out.status) - status;\n+    }\n+\n+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n+\n+    /* Generate Server header.*/\n+    if (r->headers_out.server == NULL) {\n+        h = ngx_array_push(headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"server\";\n+        h->name_len = sizeof(\"server\") - 1;\n+\n+        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n+            h->value = (u_char *) NGINX_VER;\n+            h->value_len = sizeof(NGINX_VER) - 1;\n+\n+        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n+            h->value = (u_char *) NGINX_VER_BUILD;\n+            h->value_len = sizeof(NGINX_VER_BUILD) - 1;\n+\n+        } else {\n+            h->value = (u_char *) \"nginx\";\n+            h->value_len = sizeof(\"nginx\") - 1;\n+        }\n+    }\n+\n+    /* Generate Date header. */\n+    if (r->headers_out.date == NULL) {\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"date: %V\\\"\",\n+                       &ngx_cached_http_time);\n+\n+        h = ngx_array_push(headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"date\";\n+        h->name_len = sizeof(\"date\") - 1;\n+\n+        h->value = ngx_cached_http_time.data;\n+        h->value_len = ngx_cached_http_time.len;\n+    }\n+\n+    /* Generate Content-Type header. */\n+    if (r->headers_out.content_type.len) {\n+        h = ngx_array_push(headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n+            && r->headers_out.charset.len)\n+        {\n+            len = r->headers_out.content_type.len + sizeof(\"; charset=\") - 1\n+                  + r->headers_out.charset.len;\n+\n+            tmp = ngx_pnalloc(r->pool, len);\n+            if (tmp == NULL) {\n+                return NGX_ERROR;\n+            }\n+\n+            tmp = ngx_cpymem(tmp, r->headers_out.content_type.data,\n+                             r->headers_out.content_type.len);\n+\n+            tmp = ngx_cpymem(tmp, \"; charset=\", sizeof(\"; charset=\") - 1);\n+\n+            tmp = ngx_cpymem(tmp, r->headers_out.charset.data,\n+                             r->headers_out.charset.len);\n+\n+            /* updated r->headers_out.content_type is also needed for logging */\n+\n+            r->headers_out.content_type.len = len;\n+            r->headers_out.content_type.data = tmp - len;\n+        }\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"content-type: %V\\\"\",\n+                       &r->headers_out.content_type);\n+\n+        h->name = (u_char *) \"content-type\";\n+        h->name_len = sizeof(\"content-type\") - 1;\n+\n+        h->value = r->headers_out.content_type.data;\n+        h->value_len = r->headers_out.content_type.len;\n+    }\n+\n+    /* Generate Content-Length header. */\n+    if (r->headers_out.content_length == NULL\n+        && r->headers_out.content_length_n >= 0)\n+    {\n+        h = ngx_array_push(headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"content-length\";\n+        h->name_len = sizeof(\"content-length\") - 1;\n+\n+        h->value = content_len;\n+        h->value_len =\n+            ngx_sprintf(content_len, \"%O\", r->headers_out.content_length_n) -\n+            content_len;\n+    }\n+\n+    /* Generate Last-Modified header. */\n+    if (r->headers_out.last_modified == NULL\n+        && r->headers_out.last_modified_time != -1)\n+    {\n+        h = ngx_array_push(headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        ngx_http_time(last_modified, r->headers_out.last_modified_time);\n+\n+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"last-modified: %*.s\\\"\",\n+                       sizeof(last_modified), last_modified);\n+\n+        h->name = (u_char *) \"last-modified\";\n+        h->name_len = sizeof(\"last-modified\") - 1;\n+\n+        h->value = last_modified;\n+        h->value_len = sizeof(last_modified);\n+    }\n+\n+    /* Generate Location header. */\n+    if (r->headers_out.location && r->headers_out.location->value.len) {\n+\n+        if (r->headers_out.location->value.data[0] == '/'\n+            && clcf->absolute_redirect)\n+        {\n+            if (clcf->server_name_in_redirect) {\n+                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n+                host = cscf->server_name;\n+\n+            } else if (r->headers_in.server.len) {\n+                host = r->headers_in.server;\n+\n+            } else {\n+                host.len = NGX_SOCKADDR_STRLEN;\n+                host.data = addr;\n+\n+                if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {\n+                    return NGX_ERROR;\n+                }\n+            }\n+\n+            port = ngx_inet_get_port(fc->local_sockaddr);\n+\n+            location.len = sizeof(\"https://\") - 1 + host.len\n+                           + r->headers_out.location->value.len;\n+\n+            if (clcf->port_in_redirect) {\n+\n+#if (NGX_HTTP_SSL)\n+                if (fc->ssl)\n+                    port = (port == 443) ? 0 : port;\n+                else\n+#endif\n+                    port = (port == 80) ? 0 : port;\n+\n+            } else {\n+                port = 0;\n+            }\n+\n+            if (port) {\n+                location.len += sizeof(\":65535\") - 1;\n+            }\n+\n+            location.data = ngx_pnalloc(r->pool, location.len);\n+            if (location.data == NULL) {\n+                return NGX_ERROR;\n+            }\n+\n+            tmp = ngx_cpymem(location.data, \"http\", sizeof(\"http\") - 1);\n+\n+#if (NGX_HTTP_SSL)\n+            if (fc->ssl) {\n+                *tmp++ = 's';\n+            }\n+#endif\n+\n+            *tmp++ = ':'; *tmp++ = '/'; *tmp++ = '/';\n+            tmp = ngx_cpymem(tmp, host.data, host.len);\n+\n+            if (port) {\n+                tmp = ngx_sprintf(tmp, \":%ui\", port);\n+            }\n+\n+            tmp = ngx_cpymem(tmp, r->headers_out.location->value.data,\n+                                  r->headers_out.location->value.len);\n+\n+            /* update r->headers_out.location->value for possible logging */\n+\n+            r->headers_out.location->value.len = tmp - location.data;\n+            r->headers_out.location->value.data = location.data;\n+            ngx_str_set(&r->headers_out.location->key, \"Location\");\n+        }\n+\n+        r->headers_out.location->hash = 0;\n+\n+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"location: %V\\\"\",\n+                       &r->headers_out.location->value);\n+\n+        h = ngx_array_push(headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        h->name = (u_char *) \"location\";\n+        h->name_len = sizeof(\"location\") - 1;\n+\n+        h->value = r->headers_out.location->value.data;\n+        h->value_len = r->headers_out.location->value.len;\n+    }\n+\n+#if (NGX_HTTP_GZIP)\n+    /* Generate Vary header. */\n+    if (r->gzip_vary) {\n+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                       \"http3 output header: \\\"vary: Accept-Encoding\\\"\");\n+\n+        h->name = (u_char *) \"vary\";\n+        h->name_len = sizeof(\"vary\") - 1;\n+\n+        h->value = (u_char *) \"Accept-Encoding\";\n+        h->value_len = sizeof(\"Accept-Encoding\") - 1;\n+    }\n+#endif\n+\n+    part = &r->headers_out.headers.part;\n+    header = part->elts;\n+\n+    /* Generate all other headers. */\n+    for (i = 0; /* void */; i++) {\n+\n+        if (i >= part->nelts) {\n+            if (part->next == NULL) {\n+                break;\n+            }\n+\n+            part = part->next;\n+            header = part->elts;\n+            i = 0;\n+        }\n+\n+        if (header[i].hash == 0) {\n+            continue;\n+        }\n+\n+        h = ngx_array_push(headers);\n+        if (h == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+#if (NGX_DEBUG)\n+        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                           \"http3 output header: \\\"%V: %V\\\"\",\n+                           &header[i].key, &header[i].value);\n+        }\n+#endif\n+\n+        h->name = header[i].key.data;\n+        h->name_len = header[i].key.len;\n+\n+        h->value = header[i].value.data;\n+        h->value_len = header[i].value.len;\n+    }\n+\n+    fin = r->header_only\n+          || (r->headers_out.content_length_n == 0 && !r->expect_trailers);\n+\n+    if (quiche_h3_send_response(h3c->h3, c->quic->conn, r->qstream->id,\n+                                headers->elts, headers->nelts, fin)) {\n+        ngx_array_destroy(headers);\n+        return NGX_ERROR;\n+    }\n+\n+    ngx_post_event(c->write, &ngx_posted_events);\n+\n+    ngx_array_destroy(headers);\n+\n+    return NGX_OK;\n+}\n+\n+\n+static ssize_t\n+ngx_http_v3_stream_do_send(ngx_connection_t *fc, ngx_buf_t *b, ngx_int_t fin)\n+{\n+    ssize_t                    n;\n+    ngx_connection_t          *c;\n+    ngx_http_request_t        *r;\n+    ngx_http_v3_connection_t  *h3c;\n+    ngx_http_v3_stream_t      *stream;\n+\n+    uint8_t *buf = b ? b->pos : NULL;\n+    size_t buf_len = b ? ngx_buf_size(b) : 0;\n+\n+    r = fc->data;\n+    stream = r->qstream;\n+    h3c = stream->connection;\n+    c = h3c->connection;\n+\n+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, fc->log, 0,\n+                   \"http3 stream %uz to write %uz bytes, fin=%d\",\n+                   stream->id, buf_len, fin);\n+\n+    n = quiche_h3_send_body(h3c->h3, c->quic->conn, r->qstream->id,\n+                            buf, buf_len, fin);\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, stream->connection->connection->log, 0,\n+                   \"http3 stream written %z bytes\", n);\n+\n+    if (n == QUICHE_ERR_DONE) {\n+        return NGX_AGAIN;\n+    }\n+\n+    if (n < 0) {\n+        ngx_log_error(NGX_LOG_ERR, fc->log, 0, \"stream write failed: %d\", n);\n+        return NGX_ERROR;\n+    }\n+\n+    return n;\n+}\n+\n+\n+static ngx_chain_t *\n+ngx_http_v3_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)\n+{\n+    ssize_t                n, sent;\n+    off_t                  send, prev_send;\n+    ngx_uint_t             blocked, fin;\n+\n+    ngx_http_request_t    *r;\n+    ngx_http_v3_stream_t  *stream;\n+\n+    r = fc->data;\n+    stream = r->qstream;\n+\n+    send = 0;\n+\n+    blocked = 0;\n+\n+    while (in) {\n+        prev_send = send;\n+\n+        fin = in->buf->last_buf;\n+\n+        send += ngx_buf_size(in->buf);\n+\n+        n = ngx_http_v3_stream_do_send(fc, in->buf, fin);\n+\n+        if (n == NGX_ERROR) {\n+            return NGX_CHAIN_ERROR;\n+        }\n+\n+        sent = (n == NGX_AGAIN) ? 0 : n;\n+\n+        fc->sent += sent;\n+\n+        in->buf->pos += sent;\n+\n+        if (in->buf->pos == in->buf->last) {\n+            in = in->next;\n+        }\n+\n+        if (send - prev_send != sent) {\n+            blocked = 1;\n+            break;\n+        }\n+\n+        if (fin) {\n+            stream->out_closed = 1;\n+        }\n+    }\n+\n+    if (blocked) {\n+        if (!stream->blocked) {\n+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, stream->connection->connection->log, 0,\n+                           \"http3 stream blocked %ui\", stream->id);\n+\n+            stream->blocked = 1;\n+\n+            fc->write->active = 1;\n+            fc->write->ready = 0;\n+        }\n+    }\n+\n+    ngx_post_event(stream->connection->connection->write, &ngx_posted_events);\n+\n+    return in;\n+}\n+\n+\n+void\n+ngx_http_v3_close_stream(ngx_http_v3_stream_t *stream, ngx_int_t rc)\n+{\n+    ngx_event_t               *ev;\n+    ngx_connection_t          *fc;\n+    ngx_http_v3_connection_t  *h3c;\n+\n+    h3c = stream->connection;\n+\n+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n+                   \"http3 close stream %ui\", stream->id);\n+\n+    quiche_conn_stream_shutdown(h3c->connection->quic->conn, stream->id,\n+                                QUICHE_SHUTDOWN_READ, 0);\n+\n+    ngx_rbtree_delete(&h3c->streams, &stream->node);\n+\n+    fc = stream->request->connection;\n+\n+    ngx_http_free_request(stream->request, rc);\n+\n+    ev = fc->read;\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    ev = fc->write;\n+\n+    if (ev->timer_set) {\n+        ngx_del_timer(ev);\n+    }\n+\n+    if (ev->posted) {\n+        ngx_delete_posted_event(ev);\n+    }\n+\n+    fc->data = h3c->free_fake_connections;\n+    h3c->free_fake_connections = fc;\n+\n+    h3c->processing--;\n+}\n+\n+\n+static void\n+ngx_http_v3_close_stream_handler(ngx_event_t *ev)\n+{\n+    ngx_connection_t    *fc;\n+    ngx_http_request_t  *r;\n+\n+    fc = ev->data;\n+    r = fc->data;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n+                   \"http3 close stream handler\");\n+\n+    if (ev->timedout) {\n+        ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, \"client timed out\");\n+\n+        fc->timedout = 1;\n+\n+        ngx_http_v3_close_stream(r->qstream, NGX_HTTP_REQUEST_TIME_OUT);\n+        return;\n+    }\n+\n+    ngx_http_v3_close_stream(r->qstream, 0);\n+}\n+\n+\n+static void\n+ngx_http_v3_finalize_connection(ngx_http_v3_connection_t *h3c,\n+    ngx_uint_t status)\n+{\n+    ngx_event_t             *ev;\n+    ngx_connection_t        *c, *fc;\n+    ngx_rbtree_node_t       *node, *root, *sentinel;\n+    ngx_http_request_t      *r;\n+    ngx_http_v3_stream_t    *stream;\n+\n+    c = h3c->connection;\n+\n+    quiche_conn_close(c->quic->conn, true, status, NULL, 0);\n+\n+    c->error = 1;\n+\n+    if (!h3c->processing) {\n+        ngx_http_close_connection(c);\n+        return;\n+    }\n+\n+    c->read->handler = ngx_http_empty_handler;\n+    c->write->handler = ngx_http_empty_handler;\n+\n+    sentinel = h3c->streams.sentinel;\n+\n+    /* Close all pending streams / requests. */\n+    for ( ;; ) {\n+        root = h3c->streams.root;\n+\n+        if (root == sentinel) {\n+            break;\n+        }\n+\n+        node = ngx_rbtree_min(root, sentinel);\n+\n+        stream = (ngx_http_v3_stream_t *) node;\n+\n+        r = stream->request;\n+        fc = r->connection;\n+\n+        fc->error = 1;\n+\n+        ev = fc->read;\n+\n+        ev->eof = 1;\n+        ev->handler(ev);\n+    }\n+\n+    if (h3c->processing) {\n+        return;\n+    }\n+\n+    ngx_http_close_connection(c);\n+}\n+\n+\n+static void\n+ngx_http_v3_pool_cleanup(void *data)\n+{\n+    ngx_http_v3_connection_t  *h3c = data;\n+\n+    if (h3c->h3) {\n+        quiche_h3_conn_free(h3c->h3);\n+    }\n+}\ndiff -uNr a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c\n--- a/src/http/v3/ngx_http_v3_filter_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3_filter_module.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -0,0 +1,68 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+\n+static ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf);\n+\n+\n+static ngx_http_module_t  ngx_http_v3_filter_module_ctx = {\n+    NULL,                                  /* preconfiguration */\n+    ngx_http_v3_filter_init,               /* postconfiguration */\n+\n+    NULL,                                  /* create main configuration */\n+    NULL,                                  /* init main configuration */\n+\n+    NULL,                                  /* create server configuration */\n+    NULL,                                  /* merge server configuration */\n+\n+    NULL,                                  /* create location configuration */\n+    NULL                                   /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_v3_filter_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_v3_filter_module_ctx,        /* module context */\n+    NULL,                                  /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\n+\n+\n+static ngx_int_t\n+ngx_http_v3_header_filter(ngx_http_request_t *r)\n+{\n+    if (!r->qstream) {\n+        return ngx_http_next_header_filter(r);\n+    }\n+\n+    return ngx_http_v3_send_response(r);\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_filter_init(ngx_conf_t *cf)\n+{\n+    ngx_http_next_header_filter = ngx_http_top_header_filter;\n+    ngx_http_top_header_filter = ngx_http_v3_header_filter;\n+\n+    return NGX_OK;\n+}\ndiff -uNr a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h\n--- a/src/http/v3/ngx_http_v3.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3.h\t2020-01-02 21:25:20.000000000 +0800\n@@ -0,0 +1,76 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#ifndef _NGX_HTTP_V3_H_INCLUDED_\n+#define _NGX_HTTP_V3_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+\n+#define NGX_HTTP_V3_ALPN_ADVERTISE       \"\\x05h3-18\"\n+\n+\n+typedef struct ngx_http_v3_connection_s   ngx_http_v3_connection_t;\n+\n+\n+struct ngx_http_v3_connection_s {\n+    quiche_h3_conn             *h3;\n+\n+    ngx_connection_t           *connection;\n+    ngx_http_connection_t      *http_connection;\n+\n+    ngx_pool_t                 *pool;\n+\n+    ngx_uint_t                  processing;\n+\n+    ngx_rbtree_t                streams;\n+    ngx_rbtree_node_t           streams_sentinel;\n+\n+    ngx_connection_t           *free_fake_connections;\n+};\n+\n+\n+struct ngx_http_v3_stream_s {\n+    ngx_rbtree_node_t          node;\n+\n+    uint64_t                   id;\n+\n+    ngx_http_request_t        *request;\n+\n+    ngx_http_v3_connection_t  *connection;\n+\n+    ngx_array_t               *cookies;\n+\n+    ngx_http_v3_stream_t      *next;\n+\n+    ngx_uint_t                 in_closed:1;\n+    ngx_uint_t                 out_closed:1;\n+    ngx_uint_t                 skip_data:1;\n+    ngx_uint_t                 blocked:1;\n+};\n+\n+\n+typedef struct {\n+    ngx_str_t                        name;\n+    ngx_str_t                        value;\n+} ngx_http_v3_header_t;\n+\n+\n+void ngx_http_v3_init(ngx_event_t *rev);\n+\n+ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r);\n+ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r);\n+\n+ngx_int_t ngx_http_v3_send_response(ngx_http_request_t *r);\n+\n+void ngx_http_v3_close_stream(ngx_http_v3_stream_t *stream, ngx_int_t rc);\n+\n+\n+#endif /* _NGX_HTTP_V3_H_INCLUDED_ */\ndiff -uNr a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c\n--- a/src/http/v3/ngx_http_v3_module.c\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3_module.c\t2020-01-02 21:25:20.000000000 +0800\n@@ -0,0 +1,286 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+#include <ngx_http.h>\n+#include <ngx_http_v3_module.h>\n+\n+#include <quiche.h>\n+\n+\n+static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf);\n+\n+static void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf);\n+static char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf,\n+    void *parent, void *child);\n+\n+static ngx_int_t ngx_http_v3_variable(ngx_http_request_t *r,\n+    ngx_http_variable_value_t *v, uintptr_t data);\n+\n+static void ngx_http_v3_cleanup_ctx(void *data);\n+\n+\n+static ngx_command_t  ngx_http_v3_commands[] = {\n+\n+    { ngx_string(\"http3_max_concurrent_streams\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, concurrent_streams),\n+      NULL },\n+\n+    { ngx_string(\"http3_max_requests\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_num_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_requests),\n+      NULL },\n+\n+    { ngx_string(\"http3_max_header_size\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_header_size),\n+      NULL },\n+\n+    { ngx_string(\"http3_initial_max_data\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_data),\n+      NULL },\n+\n+    { ngx_string(\"http3_initial_max_stream_data\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_size_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, max_stream_data),\n+      NULL },\n+\n+    { ngx_string(\"http3_idle_timeout\"),\n+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n+      ngx_conf_set_msec_slot,\n+      NGX_HTTP_SRV_CONF_OFFSET,\n+      offsetof(ngx_http_v3_srv_conf_t, idle_timeout),\n+      NULL },\n+\n+      ngx_null_command\n+};\n+\n+\n+static ngx_http_module_t  ngx_http_v3_module_ctx = {\n+    ngx_http_v3_add_variables,             /* preconfiguration */\n+    NULL,                                  /* postconfiguration */\n+\n+    NULL,                                  /* create main configuration */\n+    NULL,                                  /* init main configuration */\n+\n+    ngx_http_v3_create_srv_conf,           /* create server configuration */\n+    ngx_http_v3_merge_srv_conf,            /* merge server configuration */\n+\n+    NULL,                                  /* create location configuration */\n+    NULL                                   /* merge location configuration */\n+};\n+\n+\n+ngx_module_t  ngx_http_v3_module = {\n+    NGX_MODULE_V1,\n+    &ngx_http_v3_module_ctx,             /* module context */\n+    ngx_http_v3_commands,                /* module directives */\n+    NGX_HTTP_MODULE,                       /* module type */\n+    NULL,                                  /* init master */\n+    NULL,                                  /* init module */\n+    NULL,                                  /* init process */\n+    NULL,                                  /* init thread */\n+    NULL,                                  /* exit thread */\n+    NULL,                                  /* exit process */\n+    NULL,                                  /* exit master */\n+    NGX_MODULE_V1_PADDING\n+};\n+\n+\n+static ngx_http_variable_t ngx_http_v3_variables[] = {\n+\n+    { ngx_string(\"http3\"), NULL,\n+      ngx_http_v3_variable, 0,\n+      NGX_HTTP_VAR_CHANGEABLE, 0 },\n+\n+      ngx_http_null_variable\n+};\n+\n+\n+static ngx_int_t\n+ngx_http_v3_add_variables(ngx_conf_t *cf)\n+{\n+    ngx_http_variable_t *var, *v;\n+\n+    for (v = ngx_http_v3_variables; v->name.len; v++) {\n+        var = ngx_http_add_variable(cf, &v->name, v->flags);\n+        if (var == NULL) {\n+            return NGX_ERROR;\n+        }\n+\n+        var->get_handler = v->get_handler;\n+        var->data = v->data;\n+    }\n+\n+    return NGX_OK;\n+}\n+\n+\n+static void *\n+ngx_http_v3_create_srv_conf(ngx_conf_t *cf)\n+{\n+    ngx_http_v3_srv_conf_t  *h3scf;\n+\n+    h3scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t));\n+    if (h3scf == NULL) {\n+        return NULL;\n+    }\n+\n+    h3scf->idle_timeout = NGX_CONF_UNSET_MSEC;\n+    h3scf->max_data = NGX_CONF_UNSET_SIZE;\n+    h3scf->max_stream_data = NGX_CONF_UNSET_SIZE;\n+    h3scf->max_requests = NGX_CONF_UNSET_UINT;\n+    h3scf->max_header_size = NGX_CONF_UNSET_SIZE;\n+    h3scf->concurrent_streams = NGX_CONF_UNSET_UINT;\n+\n+    return h3scf;\n+}\n+\n+\n+#if (NGX_DEBUG)\n+static void\n+quiche_log(const char *line, void *argp)\n+{\n+    ngx_log_t *log = ngx_cycle->log;\n+\n+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, line);\n+}\n+#endif\n+\n+\n+static char *\n+ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n+{\n+    ngx_http_v3_srv_conf_t *prev = parent;\n+    ngx_http_v3_srv_conf_t *conf = child;\n+\n+    ngx_pool_cleanup_t  *cln;\n+\n+    ngx_conf_merge_msec_value(conf->idle_timeout,\n+                              prev->idle_timeout, 180000);\n+\n+    ngx_conf_merge_size_value(conf->max_data,\n+                              prev->max_data, 10485760);\n+\n+    ngx_conf_merge_size_value(conf->max_stream_data,\n+                              prev->max_stream_data, 1048576);\n+\n+    ngx_conf_merge_uint_value(conf->max_requests,\n+                              prev->max_requests, 1000);\n+\n+    ngx_conf_merge_size_value(conf->max_header_size,\n+                              prev->max_header_size, 16384);\n+\n+    ngx_conf_merge_uint_value(conf->concurrent_streams,\n+                              prev->concurrent_streams, 128);\n+\n+    conf->quic.log = cf->log;\n+\n+#if (NGX_DEBUG)\n+    /* Enable quiche debug logging. quiche commit ceade4 or later is required */\n+    quiche_enable_debug_logging(quiche_log, NULL);\n+#endif\n+\n+    if (ngx_quic_create_conf(&conf->quic) != NGX_OK) {\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    quiche_config_set_max_idle_timeout(conf->quic.config, conf->idle_timeout);\n+\n+    quiche_config_set_initial_max_data(conf->quic.config, conf->max_data);\n+\n+    quiche_config_set_initial_max_stream_data_bidi_remote(conf->quic.config,\n+                                                          conf->max_stream_data);\n+\n+    quiche_config_set_initial_max_stream_data_uni(conf->quic.config,\n+                                                  conf->max_stream_data);\n+\n+    quiche_config_set_initial_max_streams_bidi(conf->quic.config,\n+                                               conf->concurrent_streams);\n+\n+    /* For HTTP/3 we only need 3 unidirectional streams. */\n+    quiche_config_set_initial_max_streams_uni(conf->quic.config, 3);\n+\n+    conf->http3 = quiche_h3_config_new();\n+    if (conf->http3 == NULL) {\n+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n+                           \"failed to create HTTP/3 config\");\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    quiche_h3_config_set_max_header_list_size(conf->http3,\n+                                              conf->max_header_size);\n+\n+    cln = ngx_pool_cleanup_add(cf->pool, 0);\n+    if (cln == NULL) {\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    cln->handler = ngx_quic_cleanup_ctx;\n+    cln->data = &conf->quic;\n+\n+    cln = ngx_pool_cleanup_add(cf->pool, 0);\n+    if (cln == NULL) {\n+        return NGX_CONF_ERROR;\n+    }\n+\n+    cln->handler = ngx_http_v3_cleanup_ctx;\n+    cln->data = conf->http3;\n+\n+    return NGX_CONF_OK;\n+}\n+\n+\n+static ngx_int_t\n+ngx_http_v3_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n+    uintptr_t data)\n+{\n+    ngx_connection_t   *c;\n+\n+    v->valid = 1;\n+    v->no_cacheable = 1;\n+    v->not_found = 0;\n+\n+    c = r->connection;\n+    if (c == NULL) {\n+        return NGX_ERROR;\n+    }\n+\n+    if (c->quic != NULL) {\n+        v->len = sizeof(\"h3\") - 1;\n+        v->valid = 1;\n+        v->no_cacheable = 0;\n+        v->not_found = 0;\n+        v->data = (u_char *) \"h3\";\n+\n+        return NGX_OK;\n+    }\n+\n+    *v = ngx_http_variable_null_value;\n+    return NGX_OK;\n+}\n+\n+\n+static void\n+ngx_http_v3_cleanup_ctx(void *data)\n+{\n+    quiche_h3_config  *config = data;\n+\n+    quiche_h3_config_free(config);\n+}\ndiff -uNr a/src/http/v3/ngx_http_v3_module.h b/src/http/v3/ngx_http_v3_module.h\n--- a/src/http/v3/ngx_http_v3_module.h\t1970-01-01 08:00:00.000000000 +0800\n+++ b/src/http/v3/ngx_http_v3_module.h\t2020-01-02 21:25:20.000000000 +0800\n@@ -0,0 +1,34 @@\n+\n+/*\n+ * Copyright (C) Cloudflare, Inc.\n+ */\n+\n+\n+#ifndef _NGX_HTTP_V3_MODULE_H_INCLUDED_\n+#define _NGX_HTTP_V3_MODULE_H_INCLUDED_\n+\n+\n+#include <ngx_config.h>\n+#include <ngx_core.h>\n+\n+#include <quiche.h>\n+\n+\n+typedef struct {\n+    ngx_quic_t                      quic;\n+\n+    quiche_h3_config                *http3;\n+\n+    ngx_msec_t                      idle_timeout;\n+    size_t                          max_data;\n+    size_t                          max_stream_data;\n+    ngx_uint_t                      max_requests;\n+    ngx_uint_t                      max_header_size;\n+    ngx_uint_t                      concurrent_streams;\n+} ngx_http_v3_srv_conf_t;\n+\n+\n+extern ngx_module_t  ngx_http_v3_module;\n+\n+\n+#endif /* _NGX_HTTP_V3_MODULE_H_INCLUDED_ */\n"
  },
  {
    "path": "openssl-1.1.1.patch",
    "content": "Add TLS 1.3 Support.\nAdd BoringSSL's Equal Preference Support.\nAdd ChaCha20-Poly1305 Draft Version Support.\n\nUsing: patch -p1 < openssl-1.1.1.patch\n\ndiff --color -uNr a/crypto/evp/c_allc.c b/crypto/evp/c_allc.c\n--- a/crypto/evp/c_allc.c\t2022-11-01 20:36:10.000000000 +0800\n+++ b/crypto/evp/c_allc.c\t2022-12-25 15:28:59.857389551 +0800\n@@ -261,6 +261,7 @@\n     EVP_add_cipher(EVP_chacha20());\n # ifndef OPENSSL_NO_POLY1305\n     EVP_add_cipher(EVP_chacha20_poly1305());\n+    EVP_add_cipher(EVP_chacha20_poly1305_draft());\n # endif\n #endif\n }\ndiff --color -uNr a/crypto/evp/e_chacha20_poly1305.c b/crypto/evp/e_chacha20_poly1305.c\n--- a/crypto/evp/e_chacha20_poly1305.c\t2022-11-01 20:36:10.000000000 +0800\n+++ b/crypto/evp/e_chacha20_poly1305.c\t2022-12-25 15:28:59.857389551 +0800\n@@ -156,6 +156,7 @@\n     struct { uint64_t aad, text; } len;\n     int aad, mac_inited, tag_len, nonce_len;\n     size_t tls_payload_length;\n+    unsigned char draft:1;\n } EVP_CHACHA_AEAD_CTX;\n \n #  define NO_TLS_PAYLOAD_LENGTH ((size_t)-1)\n@@ -176,6 +177,7 @@\n     actx->aad = 0;\n     actx->mac_inited = 0;\n     actx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;\n+    actx->draft = 0;\n \n     if (iv != NULL) {\n         unsigned char temp[CHACHA_CTR_SIZE] = { 0 };\n@@ -197,6 +199,27 @@\n     return 1;\n }\n \n+static int chacha20_poly1305_draft_init_key(EVP_CIPHER_CTX *ctx,\n+   const unsigned char *inkey,\n+   const unsigned char *iv, int enc)\n+{\n+    EVP_CHACHA_AEAD_CTX *actx = aead_data(ctx);\n+\n+    if (!inkey)\n+        return 1;\n+\n+    actx->len.aad = 0;\n+    actx->len.text = 0;\n+    actx->aad = 0;\n+    actx->mac_inited = 0;\n+    actx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;\n+    actx->draft = 1;\n+\n+    chacha_init_key(ctx, inkey, NULL, enc);\n+\n+    return 1;\n+}\n+\n #  if !defined(OPENSSL_SMALL_FOOTPRINT)\n \n #   if defined(POLY1305_ASM) && (defined(__x86_64) || defined(__x86_64__) || \\\n@@ -367,10 +390,11 @@\n {\n     EVP_CHACHA_AEAD_CTX *actx = aead_data(ctx);\n     size_t rem, plen = actx->tls_payload_length;\n+    uint64_t thirteen = EVP_AEAD_TLS1_AAD_LEN;\n \n     if (!actx->mac_inited) {\n #  if !defined(OPENSSL_SMALL_FOOTPRINT)\n-        if (plen != NO_TLS_PAYLOAD_LENGTH && out != NULL)\n+        if (plen != NO_TLS_PAYLOAD_LENGTH && out != NULL && !actx->draft)\n             return chacha20_poly1305_tls_cipher(ctx, out, in, len);\n #  endif\n         actx->key.counter[0] = 0;\n@@ -397,9 +421,14 @@\n             return len;\n         } else {                                /* plain- or ciphertext */\n             if (actx->aad) {                    /* wrap up aad */\n-                if ((rem = (size_t)actx->len.aad % POLY1305_BLOCK_SIZE))\n-                    Poly1305_Update(POLY1305_ctx(actx), zero,\n-                                    POLY1305_BLOCK_SIZE - rem);\n+                if (actx->draft) {\n+                    thirteen = actx->len.aad;\n+                    Poly1305_Update(POLY1305_ctx(actx), (const unsigned char *)&thirteen, sizeof(thirteen));\n+                } else {\n+                    if ((rem = (size_t)actx->len.aad % POLY1305_BLOCK_SIZE))\n+                        Poly1305_Update(POLY1305_ctx(actx), zero,\n+                                        POLY1305_BLOCK_SIZE - rem);\n+                }\n                 actx->aad = 0;\n             }\n \n@@ -432,40 +461,52 @@\n         } is_endian = { 1 };\n         unsigned char temp[POLY1305_BLOCK_SIZE];\n \n+        if (actx->draft) {\n+            thirteen = actx->len.text;\n+            Poly1305_Update(POLY1305_ctx(actx), (const unsigned char *)&thirteen, sizeof(thirteen));\n+        }\n+\n         if (actx->aad) {                        /* wrap up aad */\n-            if ((rem = (size_t)actx->len.aad % POLY1305_BLOCK_SIZE))\n-                Poly1305_Update(POLY1305_ctx(actx), zero,\n-                                POLY1305_BLOCK_SIZE - rem);\n+            if (actx->draft) {\n+               thirteen = actx->len.aad;\n+               Poly1305_Update(POLY1305_ctx(actx), (const unsigned char *)&thirteen, sizeof(thirteen));\n+            } else {\n+                if ((rem = (size_t)actx->len.aad % POLY1305_BLOCK_SIZE))\n+                    Poly1305_Update(POLY1305_ctx(actx), zero,\n+                                    POLY1305_BLOCK_SIZE - rem);\n+            }\n             actx->aad = 0;\n         }\n \n-        if ((rem = (size_t)actx->len.text % POLY1305_BLOCK_SIZE))\n-            Poly1305_Update(POLY1305_ctx(actx), zero,\n-                            POLY1305_BLOCK_SIZE - rem);\n+        if (!actx->draft) {\n+            if ((rem = (size_t)actx->len.text % POLY1305_BLOCK_SIZE))\n+                Poly1305_Update(POLY1305_ctx(actx), zero,\n+                                POLY1305_BLOCK_SIZE - rem);\n \n-        if (is_endian.little) {\n-            Poly1305_Update(POLY1305_ctx(actx),\n-                            (unsigned char *)&actx->len, POLY1305_BLOCK_SIZE);\n-        } else {\n-            temp[0]  = (unsigned char)(actx->len.aad);\n-            temp[1]  = (unsigned char)(actx->len.aad>>8);\n-            temp[2]  = (unsigned char)(actx->len.aad>>16);\n-            temp[3]  = (unsigned char)(actx->len.aad>>24);\n-            temp[4]  = (unsigned char)(actx->len.aad>>32);\n-            temp[5]  = (unsigned char)(actx->len.aad>>40);\n-            temp[6]  = (unsigned char)(actx->len.aad>>48);\n-            temp[7]  = (unsigned char)(actx->len.aad>>56);\n-\n-            temp[8]  = (unsigned char)(actx->len.text);\n-            temp[9]  = (unsigned char)(actx->len.text>>8);\n-            temp[10] = (unsigned char)(actx->len.text>>16);\n-            temp[11] = (unsigned char)(actx->len.text>>24);\n-            temp[12] = (unsigned char)(actx->len.text>>32);\n-            temp[13] = (unsigned char)(actx->len.text>>40);\n-            temp[14] = (unsigned char)(actx->len.text>>48);\n-            temp[15] = (unsigned char)(actx->len.text>>56);\n+            if (is_endian.little) {\n+                Poly1305_Update(POLY1305_ctx(actx),\n+                                (unsigned char *)&actx->len, POLY1305_BLOCK_SIZE);\n+            } else {\n+                temp[0]  = (unsigned char)(actx->len.aad);\n+                temp[1]  = (unsigned char)(actx->len.aad>>8);\n+                temp[2]  = (unsigned char)(actx->len.aad>>16);\n+                temp[3]  = (unsigned char)(actx->len.aad>>24);\n+                temp[4]  = (unsigned char)(actx->len.aad>>32);\n+                temp[5]  = (unsigned char)(actx->len.aad>>40);\n+                temp[6]  = (unsigned char)(actx->len.aad>>48);\n+                temp[7]  = (unsigned char)(actx->len.aad>>56);\n+\n+                temp[8]  = (unsigned char)(actx->len.text);\n+                temp[9]  = (unsigned char)(actx->len.text>>8);\n+                temp[10] = (unsigned char)(actx->len.text>>16);\n+                temp[11] = (unsigned char)(actx->len.text>>24);\n+                temp[12] = (unsigned char)(actx->len.text>>32);\n+                temp[13] = (unsigned char)(actx->len.text>>40);\n+                temp[14] = (unsigned char)(actx->len.text>>48);\n+                temp[15] = (unsigned char)(actx->len.text>>56);\n \n-            Poly1305_Update(POLY1305_ctx(actx), temp, POLY1305_BLOCK_SIZE);\n+                Poly1305_Update(POLY1305_ctx(actx), temp, POLY1305_BLOCK_SIZE);\n+            }\n         }\n         Poly1305_Final(POLY1305_ctx(actx), ctx->encrypt ? actx->tag\n                                                         : temp);\n@@ -539,12 +580,14 @@\n         return 1;\n \n     case EVP_CTRL_AEAD_SET_IVLEN:\n+        if (actx->draft) return -1;\n         if (arg <= 0 || arg > CHACHA20_POLY1305_MAX_IVLEN)\n             return 0;\n         actx->nonce_len = arg;\n         return 1;\n \n     case EVP_CTRL_AEAD_SET_IV_FIXED:\n+        if (actx->draft) return -1;\n         if (arg != 12)\n             return 0;\n         actx->nonce[0] = actx->key.counter[1]\n@@ -629,9 +672,32 @@\n     NULL        /* app_data */\n };\n \n+static EVP_CIPHER chacha20_poly1305_draft = {\n+    NID_chacha20_poly1305_draft,\n+    1,                  /* block_size */\n+    CHACHA_KEY_SIZE,    /* key_len */\n+    0,                  /* iv_len, none */\n+    EVP_CIPH_FLAG_AEAD_CIPHER | EVP_CIPH_CUSTOM_IV |\n+    EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT |\n+    EVP_CIPH_CUSTOM_COPY | EVP_CIPH_FLAG_CUSTOM_CIPHER,\n+    chacha20_poly1305_draft_init_key,\n+    chacha20_poly1305_cipher,\n+    chacha20_poly1305_cleanup,\n+    0,          /* 0 moves context-specific structure allocation to ctrl */\n+    NULL,       /* set_asn1_parameters */\n+    NULL,       /* get_asn1_parameters */\n+    chacha20_poly1305_ctrl,\n+    NULL        /* app_data */\n+};\n+\n const EVP_CIPHER *EVP_chacha20_poly1305(void)\n {\n     return(&chacha20_poly1305);\n }\n+\n+const EVP_CIPHER *EVP_chacha20_poly1305_draft(void)\n+{\n+    return(&chacha20_poly1305_draft);\n+}\n # endif\n #endif\ndiff --color -uNr a/crypto/objects/obj_dat.h b/crypto/objects/obj_dat.h\n--- a/crypto/objects/obj_dat.h\t2022-11-01 20:36:10.000000000 +0800\n+++ b/crypto/objects/obj_dat.h\t2022-12-25 15:28:59.861389655 +0800\n@@ -1078,7 +1078,7 @@\n     0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x0D,       /* [ 7753] OBJ_hmacWithSHA512_256 */\n };\n \n-#define NUM_NID 1195\n+#define NUM_NID 1196\n static const ASN1_OBJECT nid_objs[NUM_NID] = {\n     {\"UNDEF\", \"undefined\", NID_undef},\n     {\"rsadsi\", \"RSA Data Security, Inc.\", NID_rsadsi, 6, &so[0]},\n@@ -2275,9 +2275,10 @@\n     {\"magma-mac\", \"magma-mac\", NID_magma_mac},\n     {\"hmacWithSHA512-224\", \"hmacWithSHA512-224\", NID_hmacWithSHA512_224, 8, &so[7745]},\n     {\"hmacWithSHA512-256\", \"hmacWithSHA512-256\", NID_hmacWithSHA512_256, 8, &so[7753]},\n+    {\"ChaCha20-Poly1305-D\", \"chacha20-poly1305-draft\", NID_chacha20_poly1305_draft},\n };\n \n-#define NUM_SN 1186\n+#define NUM_SN 1187\n static const unsigned int sn_objs[NUM_SN] = {\n      364,    /* \"AD_DVCS\" */\n      419,    /* \"AES-128-CBC\" */\n@@ -2395,6 +2396,7 @@\n      417,    /* \"CSPName\" */\n     1019,    /* \"ChaCha20\" */\n     1018,    /* \"ChaCha20-Poly1305\" */\n+    1195,    /* \"ChaCha20-Poly1305-D\" */\n      367,    /* \"CrlID\" */\n      391,    /* \"DC\" */\n       31,    /* \"DES-CBC\" */\n@@ -3467,7 +3469,7 @@\n     1093,    /* \"x509ExtAdmission\" */\n };\n \n-#define NUM_LN 1186\n+#define NUM_LN 1187\n static const unsigned int ln_objs[NUM_LN] = {\n      363,    /* \"AD Time Stamping\" */\n      405,    /* \"ANSI X9.62\" */\n@@ -3846,6 +3848,7 @@\n      883,    /* \"certificateRevocationList\" */\n     1019,    /* \"chacha20\" */\n     1018,    /* \"chacha20-poly1305\" */\n+    1195,    /* \"chacha20-poly1305-draft\" */\n       54,    /* \"challengePassword\" */\n      407,    /* \"characteristic-two-field\" */\n      395,    /* \"clearance\" */\ndiff --color -uNr a/crypto/objects/objects.txt b/crypto/objects/objects.txt\n--- a/crypto/objects/objects.txt\t2022-11-01 20:36:10.000000000 +0800\n+++ b/crypto/objects/objects.txt\t2022-12-25 15:28:59.862389681 +0800\n@@ -1534,6 +1534,7 @@\n \t\t\t: AES-192-CBC-HMAC-SHA256\t: aes-192-cbc-hmac-sha256\n \t\t\t: AES-256-CBC-HMAC-SHA256\t: aes-256-cbc-hmac-sha256\n \t\t\t: ChaCha20-Poly1305\t\t: chacha20-poly1305\n+\t\t\t: ChaCha20-Poly1305-D\t\t: chacha20-poly1305-draft\n \t\t\t: ChaCha20\t\t\t: chacha20\n \n ISO-US 10046 2 1\t: dhpublicnumber\t\t: X9.42 DH\ndiff --color -uNr a/crypto/objects/obj_mac.num b/crypto/objects/obj_mac.num\n--- a/crypto/objects/obj_mac.num\t2022-11-01 20:36:10.000000000 +0800\n+++ b/crypto/objects/obj_mac.num\t2022-12-25 15:28:59.862389681 +0800\n@@ -1192,3 +1192,4 @@\n magma_mac\t\t1192\n hmacWithSHA512_224\t\t1193\n hmacWithSHA512_256\t\t1194\n+chacha20_poly1305_draft\t\t1195\ndiff --color -uNr a/doc/man1/ciphers.pod b/doc/man1/ciphers.pod\n--- a/doc/man1/ciphers.pod\t2022-11-01 20:36:10.000000000 +0800\n+++ b/doc/man1/ciphers.pod\t2022-12-25 15:28:59.863389707 +0800\n@@ -400,6 +400,21 @@\n \n =back\n \n+=head1 EQUAL PREFERENCE GROUPS\n+\n+If configuring a server, one may also configure equal-preference groups to\n+partially respect the client's preferences when\n+B<SSL_OP_CIPHER_SERVER_PREFERENCE> is enabled. Ciphers in an equal-preference\n+group have equal priority and use the client order. This may be used to\n+enforce that AEADs are preferred but select AES-GCM vs. ChaCha20-Poly1305\n+based on client preferences. An equal-preference is specified with square\n+brackets, combining multiple selectors separated by |. For example:\n+\n+ [ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES128-GCM-SHA256]\n+ \n+ Once an equal-preference group is used, future directives must be\n+ opcode-less.\n+\n =head1 CIPHER SUITE NAMES\n \n The following lists give the SSL or TLS cipher suites names from the\ndiff --color -uNr a/include/openssl/evp.h b/include/openssl/evp.h\n--- a/include/openssl/evp.h\t2022-11-01 20:36:10.000000000 +0800\n+++ b/include/openssl/evp.h\t2022-12-25 15:28:59.864389733 +0800\n@@ -919,6 +919,7 @@\n const EVP_CIPHER *EVP_chacha20(void);\n #  ifndef OPENSSL_NO_POLY1305\n const EVP_CIPHER *EVP_chacha20_poly1305(void);\n+const EVP_CIPHER *EVP_chacha20_poly1305_draft(void);\n #  endif\n # endif\n \ndiff --color -uNr a/include/openssl/obj_mac.h b/include/openssl/obj_mac.h\n--- a/include/openssl/obj_mac.h\t2022-11-01 20:36:10.000000000 +0800\n+++ b/include/openssl/obj_mac.h\t2022-12-25 15:28:59.866389785 +0800\n@@ -4807,6 +4807,10 @@\n #define LN_chacha20_poly1305            \"chacha20-poly1305\"\n #define NID_chacha20_poly1305           1018\n \n+#define SN_chacha20_poly1305_draft              \"ChaCha20-Poly1305-D\"\n+#define LN_chacha20_poly1305_draft              \"chacha20-poly1305-draft\"\n+#define NID_chacha20_poly1305_draft             1195\n+\n #define SN_chacha20             \"ChaCha20\"\n #define LN_chacha20             \"chacha20\"\n #define NID_chacha20            1019\ndiff --color -uNr a/include/openssl/sslerr.h b/include/openssl/sslerr.h\n--- a/include/openssl/sslerr.h\t2022-11-01 20:36:10.000000000 +0800\n+++ b/include/openssl/sslerr.h\t2022-12-25 15:28:59.867389811 +0800\n@@ -603,6 +603,8 @@\n # define SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION         209\n # define SSL_R_MISSING_TMP_DH_KEY                         171\n # define SSL_R_MISSING_TMP_ECDH_KEY                       311\n+# define SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS         101\n+# define SSL_R_NESTED_GROUP                               108\n # define SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA     293\n # define SSL_R_NOT_ON_RECORD_BOUNDARY                     182\n # define SSL_R_NOT_REPLACING_CERTIFICATE                  289\n@@ -735,9 +737,11 @@\n # define SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS       239\n # define SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES           242\n # define SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES          243\n+# define SSL_R_UNEXPECTED_GROUP_CLOSE                     109\n # define SSL_R_UNEXPECTED_CCS_MESSAGE                     262\n # define SSL_R_UNEXPECTED_END_OF_EARLY_DATA               178\n # define SSL_R_UNEXPECTED_MESSAGE                         244\n+# define SSL_R_UNEXPECTED_OPERATOR_IN_GROUP               110\n # define SSL_R_UNEXPECTED_RECORD                          245\n # define SSL_R_UNINITIALIZED                              276\n # define SSL_R_UNKNOWN_ALERT_TYPE                         246\ndiff --color -uNr a/include/openssl/ssl.h b/include/openssl/ssl.h\n--- a/include/openssl/ssl.h\t2022-11-01 20:36:10.000000000 +0800\n+++ b/include/openssl/ssl.h\t2022-12-25 15:28:59.868389837 +0800\n@@ -125,6 +125,7 @@\n # define SSL_TXT_CAMELLIA256     \"CAMELLIA256\"\n # define SSL_TXT_CAMELLIA        \"CAMELLIA\"\n # define SSL_TXT_CHACHA20        \"CHACHA20\"\n+# define SSL_TXT_CHACHA20_D      \"CHACHA20-D\"\n # define SSL_TXT_GOST            \"GOST89\"\n # define SSL_TXT_ARIA            \"ARIA\"\n # define SSL_TXT_ARIA_GCM        \"ARIAGCM\"\ndiff --color -uNr a/include/openssl/tls1.h b/include/openssl/tls1.h\n--- a/include/openssl/tls1.h\t2022-11-01 20:36:10.000000000 +0800\n+++ b/include/openssl/tls1.h\t2022-12-25 15:28:59.869389863 +0800\n@@ -597,7 +597,12 @@\n # define TLS1_CK_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256   0x0300C09A\n # define TLS1_CK_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384   0x0300C09B\n \n-/* draft-ietf-tls-chacha20-poly1305-03 */\n+/* Chacha20-Poly1305-Draft ciphersuites from draft-agl-tls-chacha20poly1305-04 */\n+# define TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_D       0x0300CC13\n+# define TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_D     0x0300CC14\n+# define TLS1_CK_DHE_RSA_WITH_CHACHA20_POLY1305_D         0x0300CC15\n+\n+/* Chacha20-Poly1305 ciphersuites from RFC7905 */\n # define TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305         0x0300CCA8\n # define TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305       0x0300CCA9\n # define TLS1_CK_DHE_RSA_WITH_CHACHA20_POLY1305           0x0300CCAA\n@@ -762,6 +767,9 @@\n # define TLS1_RFC_DHE_RSA_WITH_CHACHA20_POLY1305         \"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256\"\n # define TLS1_RFC_ECDHE_RSA_WITH_CHACHA20_POLY1305       \"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\"\n # define TLS1_RFC_ECDHE_ECDSA_WITH_CHACHA20_POLY1305     \"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\"\n+# define TLS1_RFC_DHE_RSA_WITH_CHACHA20_POLY1305_D       \"OLD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256\"\n+# define TLS1_RFC_ECDHE_RSA_WITH_CHACHA20_POLY1305_D     \"OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\"\n+# define TLS1_RFC_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_D   \"OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\"\n # define TLS1_RFC_PSK_WITH_CHACHA20_POLY1305             \"TLS_PSK_WITH_CHACHA20_POLY1305_SHA256\"\n # define TLS1_RFC_ECDHE_PSK_WITH_CHACHA20_POLY1305       \"TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256\"\n # define TLS1_RFC_DHE_PSK_WITH_CHACHA20_POLY1305         \"TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256\"\n@@ -1090,7 +1098,12 @@\n # define TLS1_TXT_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256    \"ECDH-RSA-CAMELLIA128-SHA256\"\n # define TLS1_TXT_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384    \"ECDH-RSA-CAMELLIA256-SHA384\"\n \n-/* draft-ietf-tls-chacha20-poly1305-03 */\n+/* Chacha20-Poly1305-Draft ciphersuites from draft-agl-tls-chacha20poly1305-04 */\n+# define TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_D       \"ECDHE-RSA-CHACHA20-POLY1305-OLD\"\n+# define TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_D     \"ECDHE-ECDSA-CHACHA20-POLY1305-OLD\"\n+# define TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305_D         \"DHE-RSA-CHACHA20-POLY1305-OLD\"\n+\n+/* Chacha20-Poly1305 ciphersuites from RFC7905 */\n # define TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305         \"ECDHE-RSA-CHACHA20-POLY1305\"\n # define TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305       \"ECDHE-ECDSA-CHACHA20-POLY1305\"\n # define TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305           \"DHE-RSA-CHACHA20-POLY1305\"\ndiff --color -uNr a/ssl/s3_lib.c b/ssl/s3_lib.c\n--- a/ssl/s3_lib.c\t2022-11-01 20:36:10.000000000 +0800\n+++ b/ssl/s3_lib.c\t2022-12-25 15:28:59.870389889 +0800\n@@ -31,8 +31,26 @@\n };\n \n /* The list of available TLSv1.3 ciphers */\n+/* Since nginx can not set the TLS 1.3 cipher, remove it temporarily. */\n static SSL_CIPHER tls13_ciphers[] = {\n     {\n+        0,\n+    }\n+};\n+\n+/*\n+ * The list of available ciphers, mostly organized into the following\n+ * groups:\n+ *      Always there\n+ *      EC\n+ *      PSK\n+ *      SRP (within that: RSA EC PSK)\n+ *      Cipher families: Chacha/poly, Camellia, Gost, IDEA, SEED\n+ *      Weak ciphers\n+ */\n+static SSL_CIPHER ssl3_ciphers[] = {\n+    /* TLSv1.3 ciphers */\n+    {\n         1,\n         TLS1_3_RFC_AES_128_GCM_SHA256,\n         TLS1_3_RFC_AES_128_GCM_SHA256,\n@@ -111,20 +129,8 @@\n         SSL_HANDSHAKE_MAC_SHA256,\n         128,\n         128,\n-    }\n-};\n-\n-/*\n- * The list of available ciphers, mostly organized into the following\n- * groups:\n- *      Always there\n- *      EC\n- *      PSK\n- *      SRP (within that: RSA EC PSK)\n- *      Cipher families: Chacha/poly, Camellia, Gost, IDEA, SEED\n- *      Weak ciphers\n- */\n-static SSL_CIPHER ssl3_ciphers[] = {\n+    },\n+    /* List of cipher below TLSv1.3 */\n     {\n      1,\n      SSL3_TXT_RSA_NULL_MD5,\n@@ -167,7 +173,7 @@\n      SSL_aRSA,\n      SSL_3DES,\n      SSL_SHA1,\n-     SSL3_VERSION, TLS1_2_VERSION,\n+     SSL3_VERSION, TLS1_VERSION,\n      DTLS1_BAD_VER, DTLS1_2_VERSION,\n      SSL_NOT_DEFAULT | SSL_MEDIUM | SSL_FIPS,\n      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF,\n@@ -232,7 +238,7 @@\n      SSL_aRSA,\n      SSL_AES128,\n      SSL_SHA1,\n-     SSL3_VERSION, TLS1_2_VERSION,\n+     SSL3_VERSION, TLS1_VERSION,\n      DTLS1_BAD_VER, DTLS1_2_VERSION,\n      SSL_HIGH | SSL_FIPS,\n      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF,\n@@ -296,7 +302,7 @@\n      SSL_aRSA,\n      SSL_AES256,\n      SSL_SHA1,\n-     SSL3_VERSION, TLS1_2_VERSION,\n+     SSL3_VERSION, TLS1_VERSION,\n      DTLS1_BAD_VER, DTLS1_2_VERSION,\n      SSL_HIGH | SSL_FIPS,\n      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF,\n@@ -2083,6 +2089,54 @@\n      256,\n      },\n     {\n+      1,\n+      TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305_D,\n+      TLS1_RFC_DHE_RSA_WITH_CHACHA20_POLY1305_D,\n+      TLS1_CK_DHE_RSA_WITH_CHACHA20_POLY1305_D,\n+      SSL_kDHE,\n+      SSL_aRSA,\n+      SSL_CHACHA20POLY1305_D,\n+      SSL_AEAD,\n+      TLS1_2_VERSION, TLS1_2_VERSION,\n+      DTLS1_2_VERSION, DTLS1_2_VERSION,\n+      SSL_HIGH,\n+      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256,\n+      256,\n+      256,\n+     },\n+    {\n+     1,\n+     TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_D,\n+     TLS1_RFC_ECDHE_RSA_WITH_CHACHA20_POLY1305_D,\n+     TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_D,\n+     SSL_kECDHE,\n+     SSL_aRSA,\n+     SSL_CHACHA20POLY1305_D,\n+     SSL_AEAD,\n+     TLS1_2_VERSION, TLS1_2_VERSION,\n+     DTLS1_2_VERSION, DTLS1_2_VERSION,\n+     SSL_HIGH,\n+     SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256,\n+     256,\n+     256,\n+     },\n+    {\n+     1,\n+     TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_D,\n+     TLS1_RFC_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_D,\n+     TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_D,\n+     SSL_kECDHE,\n+     SSL_aECDSA,\n+     SSL_CHACHA20POLY1305_D,\n+     SSL_AEAD,\n+     TLS1_2_VERSION, TLS1_2_VERSION,\n+     DTLS1_2_VERSION, DTLS1_2_VERSION,\n+     SSL_HIGH,\n+     SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256,\n+     256,\n+     256,\n+     },\n+    {\n      1,\n      TLS1_TXT_PSK_WITH_CHACHA20_POLY1305,\n      TLS1_RFC_PSK_WITH_CHACHA20_POLY1305,\n@@ -4127,6 +4181,17 @@\n     return 1;\n }\n \n+struct ssl_cipher_preference_list_st* ssl_get_cipher_preferences(SSL *s)\n+{\n+    if (s->cipher_list != NULL)\n+        return (s->cipher_list);\n+\n+    if ((s->ctx != NULL) && (s->ctx->cipher_list != NULL))\n+        return (s->ctx->cipher_list);\n+\n+    return NULL;\n+}\n+\n /*\n  * ssl3_choose_cipher - choose a cipher from those offered by the client\n  * @s: SSL connection\n@@ -4136,16 +4201,24 @@\n  * Returns the selected cipher or NULL when no common ciphers.\n  */\n const SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt,\n-                                     STACK_OF(SSL_CIPHER) *srvr)\n+                                     struct ssl_cipher_preference_list_st\n+                                     *server_pref)\n {\n     const SSL_CIPHER *c, *ret = NULL;\n-    STACK_OF(SSL_CIPHER) *prio, *allow;\n-    int i, ii, ok, prefer_sha256 = 0;\n+    STACK_OF(SSL_CIPHER) *srvr = server_pref->ciphers, *prio, *allow;\n+    int i, ii, ok, prefer_sha256 = 0, safari_ec = 0;\n     unsigned long alg_k = 0, alg_a = 0, mask_k = 0, mask_a = 0;\n     const EVP_MD *mdsha256 = EVP_sha256();\n-#ifndef OPENSSL_NO_CHACHA\n-    STACK_OF(SSL_CIPHER) *prio_chacha = NULL;\n-#endif\n+\n+    /* in_group_flags will either be NULL, or will point to an array of\n+     * bytes which indicate equal-preference groups in the |prio| stack.\n+     * See the comment about |in_group_flags| in the\n+     * |ssl_cipher_preference_list_st| struct. */\n+    const uint8_t *in_group_flags;\n+\n+    /* group_min contains the minimal index so far found in a group, or -1\n+     * if no such value exists yet. */\n+    int group_min = -1;\n \n     /* Let's see which ciphers we can support */\n \n@@ -4172,54 +4245,13 @@\n #endif\n \n     /* SUITE-B takes precedence over server preference and ChaCha priortiy */\n-    if (tls1_suiteb(s)) {\n+    if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE || tls1_suiteb(s)) {\n         prio = srvr;\n+        in_group_flags = server_pref->in_group_flags;\n         allow = clnt;\n-    } else if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {\n-        prio = srvr;\n-        allow = clnt;\n-#ifndef OPENSSL_NO_CHACHA\n-        /* If ChaCha20 is at the top of the client preference list,\n-           and there are ChaCha20 ciphers in the server list, then\n-           temporarily prioritize all ChaCha20 ciphers in the servers list. */\n-        if (s->options & SSL_OP_PRIORITIZE_CHACHA && sk_SSL_CIPHER_num(clnt) > 0) {\n-            c = sk_SSL_CIPHER_value(clnt, 0);\n-            if (c->algorithm_enc == SSL_CHACHA20POLY1305) {\n-                /* ChaCha20 is client preferred, check server... */\n-                int num = sk_SSL_CIPHER_num(srvr);\n-                int found = 0;\n-                for (i = 0; i < num; i++) {\n-                    c = sk_SSL_CIPHER_value(srvr, i);\n-                    if (c->algorithm_enc == SSL_CHACHA20POLY1305) {\n-                        found = 1;\n-                        break;\n-                    }\n-                }\n-                if (found) {\n-                    prio_chacha = sk_SSL_CIPHER_new_reserve(NULL, num);\n-                    /* if reserve fails, then there's likely a memory issue */\n-                    if (prio_chacha != NULL) {\n-                        /* Put all ChaCha20 at the top, starting with the one we just found */\n-                        sk_SSL_CIPHER_push(prio_chacha, c);\n-                        for (i++; i < num; i++) {\n-                            c = sk_SSL_CIPHER_value(srvr, i);\n-                            if (c->algorithm_enc == SSL_CHACHA20POLY1305)\n-                                sk_SSL_CIPHER_push(prio_chacha, c);\n-                        }\n-                        /* Pull in the rest */\n-                        for (i = 0; i < num; i++) {\n-                            c = sk_SSL_CIPHER_value(srvr, i);\n-                            if (c->algorithm_enc != SSL_CHACHA20POLY1305)\n-                                sk_SSL_CIPHER_push(prio_chacha, c);\n-                        }\n-                        prio = prio_chacha;\n-                    }\n-                }\n-            }\n-        }\n-# endif\n     } else {\n         prio = clnt;\n+        in_group_flags = NULL;\n         allow = srvr;\n     }\n \n@@ -4250,14 +4282,16 @@\n     for (i = 0; i < sk_SSL_CIPHER_num(prio); i++) {\n         c = sk_SSL_CIPHER_value(prio, i);\n \n+        ok = 1;\n+\n         /* Skip ciphers not supported by the protocol version */\n         if (!SSL_IS_DTLS(s) &&\n             ((s->version < c->min_tls) || (s->version > c->max_tls)))\n-            continue;\n+            ok = 0;\n         if (SSL_IS_DTLS(s) &&\n             (DTLS_VERSION_LT(s->version, c->min_dtls) ||\n              DTLS_VERSION_GT(s->version, c->max_dtls)))\n-            continue;\n+            ok = 0;\n \n         /*\n          * Since TLS 1.3 ciphersuites can be used with any auth or\n@@ -4279,10 +4313,10 @@\n #ifndef OPENSSL_NO_PSK\n             /* with PSK there must be server callback set */\n             if ((alg_k & SSL_PSK) && s->psk_server_callback == NULL)\n-                continue;\n+                ok = 0;\n #endif                          /* OPENSSL_NO_PSK */\n \n-            ok = (alg_k & mask_k) && (alg_a & mask_a);\n+            ok = ok && (alg_k & mask_k) && (alg_a & mask_a);\n #ifdef CIPHER_DEBUG\n             fprintf(stderr, \"%d:[%08lX:%08lX:%08lX:%08lX]%p:%s\\n\", ok, alg_k,\n                     alg_a, mask_k, mask_a, (void *)c, c->name);\n@@ -4299,6 +4333,14 @@\n \n             if (!ok)\n                 continue;\n+\n+            safari_ec = 0;\n+#if !defined(OPENSSL_NO_EC)\n+            if ((alg_k & SSL_kECDHE) && (alg_a & SSL_aECDSA)) {\n+                if (s->s3->is_probably_safari)\n+                    safari_ec = 1;\n+            }\n+#endif\n         }\n         ii = sk_SSL_CIPHER_find(allow, c);\n         if (ii >= 0) {\n@@ -4306,14 +4348,7 @@\n             if (!ssl_security(s, SSL_SECOP_CIPHER_SHARED,\n                               c->strength_bits, 0, (void *)c))\n                 continue;\n-#if !defined(OPENSSL_NO_EC)\n-            if ((alg_k & SSL_kECDHE) && (alg_a & SSL_aECDSA)\n-                && s->s3->is_probably_safari) {\n-                if (!ret)\n-                    ret = sk_SSL_CIPHER_value(allow, ii);\n-                continue;\n-            }\n-#endif\n+\n             if (prefer_sha256) {\n                 const SSL_CIPHER *tmp = sk_SSL_CIPHER_value(allow, ii);\n \n@@ -4325,13 +4360,38 @@\n                     ret = tmp;\n                 continue;\n             }\n-            ret = sk_SSL_CIPHER_value(allow, ii);\n+\n+            if (in_group_flags != NULL && in_group_flags[i] == 1) {\n+                /* This element of |prio| is in a group. Update\n+                 * the minimum index found so far and continue\n+                 * looking. */\n+                if (group_min == -1 || group_min > ii)\n+                    group_min = ii;\n+            } else {\n+                if (group_min != -1 && group_min < ii)\n+                    ii = group_min;\n+                if (safari_ec) {\n+                    if (!ret)\n+                        ret = sk_SSL_CIPHER_value(allow, ii);\n+                    continue;\n+                }\n+                ret = sk_SSL_CIPHER_value(allow, ii);\n+                break;\n+            }\n+        }\n+\n+        if (in_group_flags != NULL && !in_group_flags[i] && group_min != -1) {\n+            /* We are about to leave a group, but we found a match\n+             * in it, so that's our answer. */\n+            if (safari_ec) {\n+                if (!ret)\n+                    ret = sk_SSL_CIPHER_value(allow, group_min);\n+                continue;\n+            }\n+            ret = sk_SSL_CIPHER_value(allow, group_min);\n             break;\n         }\n     }\n-#ifndef OPENSSL_NO_CHACHA\n-    sk_SSL_CIPHER_free(prio_chacha);\n-#endif\n     return ret;\n }\n \ndiff --color -uNr a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c\n--- a/ssl/ssl_ciph.c\t2022-11-01 20:36:10.000000000 +0800\n+++ b/ssl/ssl_ciph.c\t2022-12-25 15:35:32.000000000 +0800\n@@ -43,7 +43,8 @@\n #define SSL_ENC_CHACHA_IDX      19\n #define SSL_ENC_ARIA128GCM_IDX  20\n #define SSL_ENC_ARIA256GCM_IDX  21\n-#define SSL_ENC_NUM_IDX         22\n+#define SSL_ENC_CHACHA20_D_IDX  22\n+#define SSL_ENC_NUM_IDX         23\n \n /* NB: make sure indices in these tables match values above */\n \n@@ -76,6 +77,7 @@\n     {SSL_CHACHA20POLY1305, NID_chacha20_poly1305}, /* SSL_ENC_CHACHA_IDX 19 */\n     {SSL_ARIA128GCM, NID_aria_128_gcm}, /* SSL_ENC_ARIA128GCM_IDX 20 */\n     {SSL_ARIA256GCM, NID_aria_256_gcm}, /* SSL_ENC_ARIA256GCM_IDX 21 */\n+    {SSL_CHACHA20POLY1305_D, NID_chacha20_poly1305_draft}, /* SSL_ENC_CHACHA20POLY1305_IDX 22 */\n };\n \n static const EVP_CIPHER *ssl_cipher_methods[SSL_ENC_NUM_IDX];\n@@ -192,6 +194,7 @@\n     const SSL_CIPHER *cipher;\n     int active;\n     int dead;\n+    int in_group;\n     struct cipher_order_st *next, *prev;\n } CIPHER_ORDER;\n \n@@ -275,6 +278,7 @@\n     {0, SSL_TXT_CAMELLIA256, NULL, 0, 0, 0, SSL_CAMELLIA256},\n     {0, SSL_TXT_CAMELLIA, NULL, 0, 0, 0, SSL_CAMELLIA},\n     {0, SSL_TXT_CHACHA20, NULL, 0, 0, 0, SSL_CHACHA20},\n+    {0, SSL_TXT_CHACHA20_D, NULL, 0, 0, 0, SSL_CHACHA20POLY1305_D},\n \n     {0, SSL_TXT_ARIA, NULL, 0, 0, 0, SSL_ARIA},\n     {0, SSL_TXT_ARIA_GCM, NULL, 0, 0, 0, SSL_ARIA128GCM | SSL_ARIA256GCM},\n@@ -296,6 +300,7 @@\n     {0, SSL_TXT_TLSV1, NULL, 0, 0, 0, 0, 0, TLS1_VERSION},\n     {0, \"TLSv1.0\", NULL, 0, 0, 0, 0, 0, TLS1_VERSION},\n     {0, SSL_TXT_TLSV1_2, NULL, 0, 0, 0, 0, 0, TLS1_2_VERSION},\n+    {0, \"TLS13\", NULL, 0, 0, 0, 0, 0, TLS1_3_VERSION},\n \n     /* strength classes */\n     {0, SSL_TXT_LOW, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, SSL_LOW},\n@@ -681,6 +686,7 @@\n         co_list[co_list_num].next = NULL;\n         co_list[co_list_num].prev = NULL;\n         co_list[co_list_num].active = 0;\n+        co_list[co_list_num].in_group = 0;\n         co_list_num++;\n     }\n \n@@ -774,8 +780,8 @@\n                                   uint32_t alg_auth, uint32_t alg_enc,\n                                   uint32_t alg_mac, int min_tls,\n                                   uint32_t algo_strength, int rule,\n-                                  int32_t strength_bits, CIPHER_ORDER **head_p,\n-                                  CIPHER_ORDER **tail_p)\n+                                  int32_t strength_bits, int in_group,\n+                                  CIPHER_ORDER **head_p, CIPHER_ORDER **tail_p)\n {\n     CIPHER_ORDER *head, *tail, *curr, *next, *last;\n     const SSL_CIPHER *cp;\n@@ -783,9 +789,9 @@\n \n #ifdef CIPHER_DEBUG\n     fprintf(stderr,\n-            \"Applying rule %d with %08x/%08x/%08x/%08x/%08x %08x (%d)\\n\",\n+            \"Applying rule %d with %08x/%08x/%08x/%08x/%08x %08x (%d) g:%d\\n\",\n             rule, alg_mkey, alg_auth, alg_enc, alg_mac, min_tls,\n-            algo_strength, strength_bits);\n+            algo_strength, strength_bits, in_group);\n #endif\n \n     if (rule == CIPHER_DEL || rule == CIPHER_BUMP)\n@@ -862,6 +868,7 @@\n             if (!curr->active) {\n                 ll_append_tail(&head, curr, &tail);\n                 curr->active = 1;\n+                curr->in_group = in_group;\n             }\n         }\n         /* Move the added cipher to this location */\n@@ -869,6 +876,7 @@\n             /* reverse == 0 */\n             if (curr->active) {\n                 ll_append_tail(&head, curr, &tail);\n+                curr->in_group = 0;\n             }\n         } else if (rule == CIPHER_DEL) {\n             /* reverse == 1 */\n@@ -880,6 +888,7 @@\n                  */\n                 ll_append_head(&head, curr, &tail);\n                 curr->active = 0;\n+                curr->in_group = 0;\n             }\n         } else if (rule == CIPHER_BUMP) {\n             if (curr->active)\n@@ -947,8 +956,8 @@\n      */\n     for (i = max_strength_bits; i >= 0; i--)\n         if (number_uses[i] > 0)\n-            ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ORD, i, head_p,\n-                                  tail_p);\n+            ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ORD, i, 0,\n+                                  head_p, tail_p);\n \n     OPENSSL_free(number_uses);\n     return 1;\n@@ -962,7 +971,7 @@\n     uint32_t alg_mkey, alg_auth, alg_enc, alg_mac, algo_strength;\n     int min_tls;\n     const char *l, *buf;\n-    int j, multi, found, rule, retval, ok, buflen;\n+    int j, multi, found, rule, retval, ok, buflen, in_group = 0, has_group = 0;\n     uint32_t cipher_id = 0;\n     char ch;\n \n@@ -973,18 +982,66 @@\n \n         if (ch == '\\0')\n             break;              /* done */\n-        if (ch == '-') {\n+        if (in_group) {\n+            if (ch == ']') {\n+                if (!in_group) {\n+                    SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,\n+                           SSL_R_UNEXPECTED_GROUP_CLOSE);\n+                    retval = found = in_group = 0;\n+                    break;\n+                }\n+                if (*tail_p)\n+                    (*tail_p)->in_group = 0;\n+                in_group = 0;\n+                l++;\n+                continue;\n+            }\n+            if (ch == '|') {\n+                rule = CIPHER_ADD;\n+                l++;\n+                continue;\n+            } else if (!(ch >= 'a' && ch <= 'z')\n+                       && !(ch >= 'A' && ch <= 'Z')\n+                       && !(ch >= '0' && ch <= '9')) {\n+                SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,\n+                       SSL_R_UNEXPECTED_OPERATOR_IN_GROUP);\n+                retval = found = in_group = 0;\n+                break;\n+            } else {\n+                rule = CIPHER_ADD;\n+            }\n+        } else if (ch == '-') {\n             rule = CIPHER_DEL;\n             l++;\n         } else if (ch == '+') {\n             rule = CIPHER_ORD;\n             l++;\n+        } else if (ch == '!' && has_group) {\n+            SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,\n+                   SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS);\n+            retval = found = in_group = 0;\n+            break;\n         } else if (ch == '!') {\n             rule = CIPHER_KILL;\n             l++;\n+        } else if (ch == '@' && has_group) {\n+            SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,\n+                   SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS);\n+            retval = found = in_group = 0;\n+            break;\n         } else if (ch == '@') {\n             rule = CIPHER_SPECIAL;\n             l++;\n+        } else if (ch == '[') {\n+            if (in_group) {\n+                SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR, SSL_R_NESTED_GROUP);\n+                retval = found = in_group = 0;\n+                break;\n+            }\n+            in_group = 1;\n+            has_group = 1;\n+            l++;\n+            continue;\n         } else {\n             rule = CIPHER_ADD;\n         }\n@@ -1009,7 +1066,7 @@\n             while (((ch >= 'A') && (ch <= 'Z')) ||\n                    ((ch >= '0') && (ch <= '9')) ||\n                    ((ch >= 'a') && (ch <= 'z')) ||\n-                   (ch == '-') || (ch == '.') || (ch == '='))\n+                   (ch == '-') || (ch == '.') || (ch == '=') || (ch == '_'))\n #else\n             while (isalnum((unsigned char)ch) || (ch == '-') || (ch == '.')\n                    || (ch == '='))\n@@ -1026,7 +1083,9 @@\n                  * alphanumeric, so we call this an error.\n                  */\n                 SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR, SSL_R_INVALID_COMMAND);\n-                return 0;\n+                retval = found = in_group = 0;\n+                l++;\n+                break;\n             }\n \n             if (rule == CIPHER_SPECIAL) {\n@@ -1203,8 +1262,8 @@\n         } else if (found) {\n             ssl_cipher_apply_rule(cipher_id,\n                                   alg_mkey, alg_auth, alg_enc, alg_mac,\n-                                  min_tls, algo_strength, rule, -1, head_p,\n-                                  tail_p);\n+                                  min_tls, algo_strength, rule, -1, in_group,\n+                                  head_p, tail_p);\n         } else {\n             while ((*l != '\\0') && !ITEM_SEP(*l))\n                 l++;\n@@ -1213,6 +1272,11 @@\n             break;              /* done */\n     }\n \n+    if (in_group) {\n+        SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR, SSL_R_INVALID_COMMAND);\n+        retval = 0;\n+    }\n+\n     return retval;\n }\n \n@@ -1376,7 +1440,7 @@\n     int ret = set_ciphersuites(&(ctx->tls13_ciphersuites), str);\n \n     if (ret && ctx->cipher_list != NULL)\n-        return update_cipher_list(&ctx->cipher_list, &ctx->cipher_list_by_id,\n+        return update_cipher_list(&ctx->cipher_list->ciphers, &ctx->cipher_list_by_id,\n                                   ctx->tls13_ciphersuites);\n \n     return ret;\n@@ -1389,10 +1453,10 @@\n \n     if (s->cipher_list == NULL) {\n         if ((cipher_list = SSL_get_ciphers(s)) != NULL)\n-            s->cipher_list = sk_SSL_CIPHER_dup(cipher_list);\n+            s->cipher_list->ciphers = sk_SSL_CIPHER_dup(cipher_list);\n     }\n     if (ret && s->cipher_list != NULL)\n-        return update_cipher_list(&s->cipher_list, &s->cipher_list_by_id,\n+        return update_cipher_list(&s->cipher_list->ciphers, &s->cipher_list_by_id,\n                                   s->tls13_ciphersuites);\n \n     return ret;\n@@ -1400,17 +1464,20 @@\n \n STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method,\n                                              STACK_OF(SSL_CIPHER) *tls13_ciphersuites,\n-                                             STACK_OF(SSL_CIPHER) **cipher_list,\n+                                             struct ssl_cipher_preference_list_st **cipher_list,\n                                              STACK_OF(SSL_CIPHER) **cipher_list_by_id,\n                                              const char *rule_str,\n                                              CERT *c)\n {\n-    int ok, num_of_ciphers, num_of_alias_max, num_of_group_aliases, i;\n+    int ok, num_of_ciphers, num_of_alias_max, num_of_group_aliases;\n     uint32_t disabled_mkey, disabled_auth, disabled_enc, disabled_mac;\n-    STACK_OF(SSL_CIPHER) *cipherstack;\n+    STACK_OF(SSL_CIPHER) *cipherstack = NULL;\n     const char *rule_p;\n     CIPHER_ORDER *co_list = NULL, *head = NULL, *tail = NULL, *curr;\n     const SSL_CIPHER **ca_list = NULL;\n+    uint8_t *in_group_flags = NULL;\n+    unsigned int num_in_group_flags = 0;\n+    struct ssl_cipher_preference_list_st *pref_list = NULL;\n \n     /*\n      * Return with error if nothing to do.\n@@ -1459,16 +1526,16 @@\n      * preference).\n      */\n     ssl_cipher_apply_rule(0, SSL_kECDHE, SSL_aECDSA, 0, 0, 0, 0, CIPHER_ADD,\n-                          -1, &head, &tail);\n-    ssl_cipher_apply_rule(0, SSL_kECDHE, 0, 0, 0, 0, 0, CIPHER_ADD, -1, &head,\n-                          &tail);\n-    ssl_cipher_apply_rule(0, SSL_kECDHE, 0, 0, 0, 0, 0, CIPHER_DEL, -1, &head,\n-                          &tail);\n+                          -1, 0, &head, &tail);\n+    ssl_cipher_apply_rule(0, SSL_kECDHE, 0, 0, 0, 0, 0, CIPHER_ADD, -1, 0,\n+                          &head, &tail);\n+    ssl_cipher_apply_rule(0, SSL_kECDHE, 0, 0, 0, 0, 0, CIPHER_DEL, -1, 0,\n+                          &head, &tail);\n \n     /* Within each strength group, we prefer GCM over CHACHA... */\n-    ssl_cipher_apply_rule(0, 0, 0, SSL_AESGCM, 0, 0, 0, CIPHER_ADD, -1,\n+    ssl_cipher_apply_rule(0, 0, 0, SSL_AESGCM, 0, 0, 0, CIPHER_ADD, -1, 0,\n                           &head, &tail);\n-    ssl_cipher_apply_rule(0, 0, 0, SSL_CHACHA20, 0, 0, 0, CIPHER_ADD, -1,\n+    ssl_cipher_apply_rule(0, 0, 0, SSL_CHACHA20, 0, 0, 0, CIPHER_ADD, -1, 0,\n                           &head, &tail);\n \n     /*\n@@ -1477,13 +1544,13 @@\n      * strength.\n      */\n     ssl_cipher_apply_rule(0, 0, 0, SSL_AES ^ SSL_AESGCM, 0, 0, 0, CIPHER_ADD,\n-                          -1, &head, &tail);\n+                          -1, 0, &head, &tail);\n \n     /* Temporarily enable everything else for sorting */\n-    ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ADD, -1, &head, &tail);\n+    ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ADD, -1, 0, &head, &tail);\n \n     /* Low priority for MD5 */\n-    ssl_cipher_apply_rule(0, 0, 0, 0, SSL_MD5, 0, 0, CIPHER_ORD, -1, &head,\n+    ssl_cipher_apply_rule(0, 0, 0, 0, SSL_MD5, 0, 0, CIPHER_ORD, -1, 0, &head,\n                           &tail);\n \n     /*\n@@ -1491,16 +1558,16 @@\n      * disabled. (For applications that allow them, they aren't too bad, but\n      * we prefer authenticated ciphers.)\n      */\n-    ssl_cipher_apply_rule(0, 0, SSL_aNULL, 0, 0, 0, 0, CIPHER_ORD, -1, &head,\n+    ssl_cipher_apply_rule(0, 0, SSL_aNULL, 0, 0, 0, 0, CIPHER_ORD, -1, 0, &head,\n                           &tail);\n \n-    ssl_cipher_apply_rule(0, SSL_kRSA, 0, 0, 0, 0, 0, CIPHER_ORD, -1, &head,\n+    ssl_cipher_apply_rule(0, SSL_kRSA, 0, 0, 0, 0, 0, CIPHER_ORD, -1, 0, &head,\n                           &tail);\n-    ssl_cipher_apply_rule(0, SSL_kPSK, 0, 0, 0, 0, 0, CIPHER_ORD, -1, &head,\n+    ssl_cipher_apply_rule(0, SSL_kPSK, 0, 0, 0, 0, 0, CIPHER_ORD, -1, 0, &head,\n                           &tail);\n \n     /* RC4 is sort-of broken -- move to the end */\n-    ssl_cipher_apply_rule(0, 0, 0, SSL_RC4, 0, 0, 0, CIPHER_ORD, -1, &head,\n+    ssl_cipher_apply_rule(0, 0, 0, SSL_RC4, 0, 0, 0, CIPHER_ORD, -1, 0, &head,\n                           &tail);\n \n     /*\n@@ -1516,7 +1583,7 @@\n      * Partially overrule strength sort to prefer TLS 1.2 ciphers/PRFs.\n      * TODO(openssl-team): is there an easier way to accomplish all this?\n      */\n-    ssl_cipher_apply_rule(0, 0, 0, 0, 0, TLS1_2_VERSION, 0, CIPHER_BUMP, -1,\n+    ssl_cipher_apply_rule(0, 0, 0, 0, 0, TLS1_2_VERSION, 0, CIPHER_BUMP, -1, 0,\n                           &head, &tail);\n \n     /*\n@@ -1532,15 +1599,18 @@\n      * Because we now bump ciphers to the top of the list, we proceed in\n      * reverse order of preference.\n      */\n-    ssl_cipher_apply_rule(0, 0, 0, 0, SSL_AEAD, 0, 0, CIPHER_BUMP, -1,\n+    ssl_cipher_apply_rule(0, 0, 0, 0, SSL_AEAD, 0, 0, CIPHER_BUMP, -1, 0,\n                           &head, &tail);\n     ssl_cipher_apply_rule(0, SSL_kDHE | SSL_kECDHE, 0, 0, 0, 0, 0,\n-                          CIPHER_BUMP, -1, &head, &tail);\n+                          CIPHER_BUMP, -1, 0, &head, &tail);\n     ssl_cipher_apply_rule(0, SSL_kDHE | SSL_kECDHE, 0, 0, SSL_AEAD, 0, 0,\n-                          CIPHER_BUMP, -1, &head, &tail);\n+                          CIPHER_BUMP, -1, 0, &head, &tail);\n+\n+    ssl_cipher_apply_rule(0, 0, 0, 0, 0, TLS1_3_VERSION, 0, CIPHER_BUMP, -1, 0,\n+                          &head, &tail);\n \n     /* Now disable everything (maintaining the ordering!) */\n-    ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_DEL, -1, &head, &tail);\n+    ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_DEL, -1, 0, &head, &tail);\n \n     /*\n      * We also need cipher aliases for selecting based on the rule_str.\n@@ -1554,9 +1624,8 @@\n     num_of_alias_max = num_of_ciphers + num_of_group_aliases + 1;\n     ca_list = OPENSSL_malloc(sizeof(*ca_list) * num_of_alias_max);\n     if (ca_list == NULL) {\n-        OPENSSL_free(co_list);\n         SSLerr(SSL_F_SSL_CREATE_CIPHER_LIST, ERR_R_MALLOC_FAILURE);\n-        return NULL;          /* Failure */\n+        goto err;               /* Failure */\n     }\n     ssl_cipher_collect_aliases(ca_list, num_of_group_aliases,\n                                disabled_mkey, disabled_auth, disabled_enc,\n@@ -1581,29 +1650,19 @@\n \n     OPENSSL_free(ca_list);      /* Not needed anymore */\n \n-    if (!ok) {                  /* Rule processing failure */\n-        OPENSSL_free(co_list);\n-        return NULL;\n-    }\n+    if (!ok)\n+        goto err;               /* Rule processing failure */\n \n     /*\n      * Allocate new \"cipherstack\" for the result, return with error\n      * if we cannot get one.\n      */\n-    if ((cipherstack = sk_SSL_CIPHER_new_null()) == NULL) {\n-        OPENSSL_free(co_list);\n-        return NULL;\n-    }\n+    if ((cipherstack = sk_SSL_CIPHER_new_null()) == NULL)\n+        goto err;\n \n-    /* Add TLSv1.3 ciphers first - we always prefer those if possible */\n-    for (i = 0; i < sk_SSL_CIPHER_num(tls13_ciphersuites); i++) {\n-        if (!sk_SSL_CIPHER_push(cipherstack,\n-                                sk_SSL_CIPHER_value(tls13_ciphersuites, i))) {\n-            OPENSSL_free(co_list);\n-            sk_SSL_CIPHER_free(cipherstack);\n-            return NULL;\n-        }\n-    }\n+    in_group_flags = OPENSSL_malloc(num_of_ciphers);\n+    if (!in_group_flags)\n+        goto err;\n \n     /*\n      * The cipher selection for the list is done. The ciphers are added\n@@ -1611,26 +1670,50 @@\n      */\n     for (curr = head; curr != NULL; curr = curr->next) {\n         if (curr->active) {\n-            if (!sk_SSL_CIPHER_push(cipherstack, curr->cipher)) {\n-                OPENSSL_free(co_list);\n-                sk_SSL_CIPHER_free(cipherstack);\n-                return NULL;\n-            }\n+            if (!sk_SSL_CIPHER_push(cipherstack, curr->cipher))\n+                goto err;\n+            in_group_flags[num_in_group_flags++] = curr->in_group;\n #ifdef CIPHER_DEBUG\n             fprintf(stderr, \"<%s>\\n\", curr->cipher->name);\n #endif\n         }\n     }\n+\n     OPENSSL_free(co_list);      /* Not needed any longer */\n+    co_list = NULL;\n \n-    if (!update_cipher_list_by_id(cipher_list_by_id, cipherstack)) {\n-        sk_SSL_CIPHER_free(cipherstack);\n-        return NULL;\n-    }\n-    sk_SSL_CIPHER_free(*cipher_list);\n-    *cipher_list = cipherstack;\n+    if (!update_cipher_list_by_id(cipher_list_by_id, cipherstack))\n+        goto err;\n+\n+    pref_list = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st));\n+    if (!pref_list)\n+        goto err;\n+    pref_list->ciphers = cipherstack;\n+    pref_list->in_group_flags = OPENSSL_malloc(num_in_group_flags);\n+    if (!pref_list->in_group_flags)\n+        goto err;\n+    memcpy(pref_list->in_group_flags, in_group_flags, num_in_group_flags);\n+    OPENSSL_free(in_group_flags);\n+    in_group_flags = NULL;\n+    if (*cipher_list != NULL)\n+        ssl_cipher_preference_list_free(*cipher_list);\n+    *cipher_list = pref_list;\n+    pref_list = NULL;\n \n     return cipherstack;\n+\n+err:\n+    if (co_list)\n+        OPENSSL_free(co_list);\n+    if (in_group_flags)\n+        OPENSSL_free(in_group_flags);\n+    if (cipherstack)\n+        sk_SSL_CIPHER_free(cipherstack);\n+    if (pref_list && pref_list->in_group_flags)\n+        OPENSSL_free(pref_list->in_group_flags);\n+    if (pref_list)\n+        OPENSSL_free(pref_list);\n+    return NULL;\n }\n \n char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf, int len)\n@@ -1791,6 +1874,9 @@\n     case SSL_CHACHA20POLY1305:\n         enc = \"CHACHA20/POLY1305(256)\";\n         break;\n+    case SSL_CHACHA20POLY1305_D:\n+        enc = \"CHACHA20/POLY1305-Draft(256)\";\n+        break;\n     default:\n         enc = \"unknown\";\n         break;\n@@ -2115,7 +2201,7 @@\n         out = EVP_CCM_TLS_EXPLICIT_IV_LEN + 16;\n     } else if (c->algorithm_enc & (SSL_AES128CCM8 | SSL_AES256CCM8)) {\n         out = EVP_CCM_TLS_EXPLICIT_IV_LEN + 8;\n-    } else if (c->algorithm_enc & SSL_CHACHA20POLY1305) {\n+    } else if (c->algorithm_enc & (SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_D)) {\n         out = 16;\n     } else if (c->algorithm_mac & SSL_AEAD) {\n         /* We're supposed to have handled all the AEAD modes above */\ndiff --color -uNr a/ssl/ssl_err.c b/ssl/ssl_err.c\n--- a/ssl/ssl_err.c\t2022-11-01 20:36:10.000000000 +0800\n+++ b/ssl/ssl_err.c\t2022-12-25 15:28:59.873389966 +0800\n@@ -968,6 +968,9 @@\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_DH_KEY), \"missing tmp dh key\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_ECDH_KEY),\n     \"missing tmp ecdh key\"},\n+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS),\n+    \"mixed special operator with groups\"},\n+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NESTED_GROUP), \"nested group\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA),\n     \"mixed handshake and non handshake data\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_ON_RECORD_BOUNDARY),\n@@ -1206,11 +1209,14 @@\n     \"unable to load ssl3 md5 routines\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES),\n     \"unable to load ssl3 sha1 routines\"},\n+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_GROUP_CLOSE), \"unexpected group close\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_CCS_MESSAGE),\n     \"unexpected ccs message\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_END_OF_EARLY_DATA),\n     \"unexpected end of early data\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_MESSAGE), \"unexpected message\"},\n+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_OPERATOR_IN_GROUP),\n+    \"unexpected operator in group\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_RECORD), \"unexpected record\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNINITIALIZED), \"uninitialized\"},\n     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_ALERT_TYPE), \"unknown alert type\"},\ndiff --color -uNr a/ssl/ssl_lib.c b/ssl/ssl_lib.c\n--- a/ssl/ssl_lib.c\t2022-11-01 20:36:10.000000000 +0800\n+++ b/ssl/ssl_lib.c\t2022-12-25 15:28:59.875390018 +0800\n@@ -1128,6 +1128,71 @@\n     return X509_VERIFY_PARAM_set1(ssl->param, vpm);\n }\n \n+void ssl_cipher_preference_list_free(struct ssl_cipher_preference_list_st\n+                                     *cipher_list)\n+{\n+    sk_SSL_CIPHER_free(cipher_list->ciphers);\n+    OPENSSL_free(cipher_list->in_group_flags);\n+    OPENSSL_free(cipher_list);\n+}\n+\n+struct ssl_cipher_preference_list_st*\n+ssl_cipher_preference_list_dup(struct ssl_cipher_preference_list_st\n+                               *cipher_list)\n+{\n+    struct ssl_cipher_preference_list_st* ret = NULL;\n+    size_t n = sk_SSL_CIPHER_num(cipher_list->ciphers);\n+\n+    ret = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st));\n+    if (!ret)\n+        goto err;\n+    ret->ciphers = NULL;\n+    ret->in_group_flags = NULL;\n+    ret->ciphers = sk_SSL_CIPHER_dup(cipher_list->ciphers);\n+    if (!ret->ciphers)\n+        goto err;\n+    ret->in_group_flags = OPENSSL_malloc(n);\n+    if (!ret->in_group_flags)\n+        goto err;\n+    memcpy(ret->in_group_flags, cipher_list->in_group_flags, n);\n+    return ret;\n+\n+err:\n+   if (ret->ciphers)\n+       sk_SSL_CIPHER_free(ret->ciphers);\n+   if (ret)\n+       OPENSSL_free(ret);\n+   return NULL;\n+}\n+\n+struct ssl_cipher_preference_list_st*\n+ssl_cipher_preference_list_from_ciphers(STACK_OF(SSL_CIPHER) *ciphers)\n+{\n+    struct ssl_cipher_preference_list_st* ret = NULL;\n+    size_t n = sk_SSL_CIPHER_num(ciphers);\n+\n+    ret = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st));\n+    if (!ret)\n+        goto err;\n+    ret->ciphers = NULL;\n+    ret->in_group_flags = NULL;\n+    ret->ciphers = sk_SSL_CIPHER_dup(ciphers);\n+    if (!ret->ciphers)\n+        goto err;\n+    ret->in_group_flags = OPENSSL_malloc(n);\n+    if (!ret->in_group_flags)\n+        goto err;\n+    memset(ret->in_group_flags, 0, n);\n+    return ret;\n+\n+err:\n+    if (ret->ciphers)\n+        sk_SSL_CIPHER_free(ret->ciphers);\n+    if (ret)\n+        OPENSSL_free(ret);\n+    return NULL;\n+}\n+\n X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx)\n {\n     return ctx->param;\n@@ -1168,7 +1233,8 @@\n     BUF_MEM_free(s->init_buf);\n \n     /* add extra stuff */\n-    sk_SSL_CIPHER_free(s->cipher_list);\n+    if (s->cipher_list != NULL)\n+        ssl_cipher_preference_list_free(s->cipher_list);\n     sk_SSL_CIPHER_free(s->cipher_list_by_id);\n     sk_SSL_CIPHER_free(s->tls13_ciphersuites);\n     sk_SSL_CIPHER_free(s->peer_ciphers);\n@@ -2466,9 +2532,9 @@\n {\n     if (s != NULL) {\n         if (s->cipher_list != NULL) {\n-            return s->cipher_list;\n+            return (s->cipher_list->ciphers);\n         } else if ((s->ctx != NULL) && (s->ctx->cipher_list != NULL)) {\n-            return s->ctx->cipher_list;\n+            return (s->ctx->cipher_list->ciphers);\n         }\n     }\n     return NULL;\n@@ -2542,8 +2608,8 @@\n  * preference */\n STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx)\n {\n-    if (ctx != NULL)\n-        return ctx->cipher_list;\n+    if (ctx != NULL && ctx->cipher_list != NULL)\n+        return ctx->cipher_list->ciphers;\n     return NULL;\n }\n \n@@ -3087,7 +3153,7 @@\n                                 ret->tls13_ciphersuites,\n                                 &ret->cipher_list, &ret->cipher_list_by_id,\n                                 SSL_DEFAULT_CIPHER_LIST, ret->cert)\n-        || sk_SSL_CIPHER_num(ret->cipher_list) <= 0) {\n+        || sk_SSL_CIPHER_num(ret->cipher_list->ciphers) <= 0) {\n         SSLerr(SSL_F_SSL_CTX_NEW, SSL_R_LIBRARY_HAS_NO_CIPHERS);\n         goto err2;\n     }\n@@ -3263,7 +3329,7 @@\n #ifndef OPENSSL_NO_CT\n     CTLOG_STORE_free(a->ctlog_store);\n #endif\n-    sk_SSL_CIPHER_free(a->cipher_list);\n+    ssl_cipher_preference_list_free(a->cipher_list);\n     sk_SSL_CIPHER_free(a->cipher_list_by_id);\n     sk_SSL_CIPHER_free(a->tls13_ciphersuites);\n     ssl_cert_free(a->cert);\n@@ -3929,13 +3995,15 @@\n \n     /* dup the cipher_list and cipher_list_by_id stacks */\n     if (s->cipher_list != NULL) {\n-        if ((ret->cipher_list = sk_SSL_CIPHER_dup(s->cipher_list)) == NULL)\n+        ret->cipher_list = ssl_cipher_preference_list_dup(s->cipher_list);\n+        if (ret->cipher_list == NULL)\n             goto err;\n     }\n-    if (s->cipher_list_by_id != NULL)\n-        if ((ret->cipher_list_by_id = sk_SSL_CIPHER_dup(s->cipher_list_by_id))\n-            == NULL)\n+    if (s->cipher_list_by_id != NULL) {\n+        ret->cipher_list_by_id = sk_SSL_CIPHER_dup(s->cipher_list_by_id);\n+        if (ret->cipher_list_by_id == NULL)\n             goto err;\n+    }\n \n     /* Dup the client_CA list */\n     if (!dup_ca_names(&ret->ca_names, s->ca_names)\ndiff --color -uNr a/ssl/ssl_local.h b/ssl/ssl_local.h\n--- a/ssl/ssl_local.h\t2022-11-01 20:36:10.000000000 +0800\n+++ b/ssl/ssl_local.h\t2022-12-25 15:28:59.876390044 +0800\n@@ -230,12 +230,13 @@\n # define SSL_CHACHA20POLY1305    0x00080000U\n # define SSL_ARIA128GCM          0x00100000U\n # define SSL_ARIA256GCM          0x00200000U\n+# define SSL_CHACHA20POLY1305_D  0x00400000U\n \n # define SSL_AESGCM              (SSL_AES128GCM | SSL_AES256GCM)\n # define SSL_AESCCM              (SSL_AES128CCM | SSL_AES256CCM | SSL_AES128CCM8 | SSL_AES256CCM8)\n # define SSL_AES                 (SSL_AES128|SSL_AES256|SSL_AESGCM|SSL_AESCCM)\n # define SSL_CAMELLIA            (SSL_CAMELLIA128|SSL_CAMELLIA256)\n-# define SSL_CHACHA20            (SSL_CHACHA20POLY1305)\n+# define SSL_CHACHA20            (SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_D)\n # define SSL_ARIAGCM             (SSL_ARIA128GCM | SSL_ARIA256GCM)\n # define SSL_ARIA                (SSL_ARIAGCM)\n \n@@ -732,9 +733,46 @@\n     unsigned char tick_aes_key[TLSEXT_TICK_KEY_LENGTH];\n } SSL_CTX_EXT_SECURE;\n \n+/* ssl_cipher_preference_list_st contains a list of SSL_CIPHERs with\n+ * equal-preference groups. For TLS clients, the groups are moot because the\n+ * server picks the cipher and groups cannot be expressed on the wire. However,\n+ * for servers, the equal-preference groups allow the client's preferences to\n+ * be partially respected. (This only has an effect with\n+ * SSL_OP_CIPHER_SERVER_PREFERENCE).\n+ *\n+ * The equal-preference groups are expressed by grouping SSL_CIPHERs together.\n+ * All elements of a group have the same priority: no ordering is expressed\n+ * within a group.\n+ *\n+ * The values in |ciphers| are in one-to-one correspondence with\n+ * |in_group_flags|. (That is, sk_SSL_CIPHER_num(ciphers) is the number of\n+ * bytes in |in_group_flags|.) The bytes in |in_group_flags| are either 1, to\n+ * indicate that the corresponding SSL_CIPHER is not the last element of a\n+ * group, or 0 to indicate that it is.\n+ *\n+ * For example, if |in_group_flags| contains all zeros then that indicates a\n+ * traditional, fully-ordered preference. Every SSL_CIPHER is the last element\n+ * of the group (i.e. they are all in a one-element group).\n+ *\n+ * For a more complex example, consider:\n+ *   ciphers:        A  B  C  D  E  F\n+ *   in_group_flags: 1  1  0  0  1  0\n+ *\n+ * That would express the following, order:\n+ *\n+ *    A         E\n+ *    B -> D -> F\n+ *    C\n+ */\n+struct ssl_cipher_preference_list_st {\n+   STACK_OF(SSL_CIPHER) *ciphers;\n+   uint8_t *in_group_flags;\n+};\n+\n+\n struct ssl_ctx_st {\n     const SSL_METHOD *method;\n-    STACK_OF(SSL_CIPHER) *cipher_list;\n+    struct ssl_cipher_preference_list_st *cipher_list;\n     /* same as above but sorted for lookup */\n     STACK_OF(SSL_CIPHER) *cipher_list_by_id;\n     /* TLSv1.3 specific ciphersuites */\n@@ -1130,7 +1168,7 @@\n     SSL_DANE dane;\n     /* crypto */\n     STACK_OF(SSL_CIPHER) *peer_ciphers;\n-    STACK_OF(SSL_CIPHER) *cipher_list;\n+    struct ssl_cipher_preference_list_st *cipher_list;\n     STACK_OF(SSL_CIPHER) *cipher_list_by_id;\n     /* TLSv1.3 specific ciphersuites */\n     STACK_OF(SSL_CIPHER) *tls13_ciphersuites;\n@@ -2268,7 +2306,7 @@\n                                  const SSL_CIPHER *const *bp);\n __owur STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method,\n                                                     STACK_OF(SSL_CIPHER) *tls13_ciphersuites,\n-                                                    STACK_OF(SSL_CIPHER) **cipher_list,\n+                                                    struct ssl_cipher_preference_list_st **cipher_list,\n                                                     STACK_OF(SSL_CIPHER) **cipher_list_by_id,\n                                                     const char *rule_str,\n                                                     CERT *c);\n@@ -2278,6 +2316,13 @@\n                                 STACK_OF(SSL_CIPHER) **scsvs, int sslv2format,\n                                 int fatal);\n void ssl_update_cache(SSL *s, int mode);\n+struct ssl_cipher_preference_list_st* ssl_cipher_preference_list_dup(\n+        struct ssl_cipher_preference_list_st *cipher_list);\n+void ssl_cipher_preference_list_free(\n+        struct ssl_cipher_preference_list_st *cipher_list);\n+struct ssl_cipher_preference_list_st* ssl_cipher_preference_list_from_ciphers(\n+        STACK_OF(SSL_CIPHER) *ciphers);\n+struct ssl_cipher_preference_list_st* ssl_get_cipher_preferences(SSL *s);\n __owur int ssl_cipher_get_evp(const SSL_SESSION *s, const EVP_CIPHER **enc,\n                               const EVP_MD **md, int *mac_pkey_type,\n                               size_t *mac_secret_size, SSL_COMP **comp,\n@@ -2363,7 +2408,7 @@\n                                             CERT_PKEY *cpk);\n __owur const SSL_CIPHER *ssl3_choose_cipher(SSL *ssl,\n                                             STACK_OF(SSL_CIPHER) *clnt,\n-                                            STACK_OF(SSL_CIPHER) *srvr);\n+                                            struct ssl_cipher_preference_list_st *srvr);\n __owur int ssl3_digest_cached_records(SSL *s, int keep);\n __owur int ssl3_new(SSL *s);\n void ssl3_free(SSL *s);\ndiff --color -uNr a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c\n--- a/ssl/statem/statem_srvr.c\t2022-11-01 20:36:10.000000000 +0800\n+++ b/ssl/statem/statem_srvr.c\t2022-12-25 15:28:59.877390070 +0800\n@@ -1773,7 +1773,7 @@\n     /* For TLSv1.3 we must select the ciphersuite *before* session resumption */\n     if (SSL_IS_TLS13(s)) {\n         const SSL_CIPHER *cipher =\n-            ssl3_choose_cipher(s, ciphers, SSL_get_ciphers(s));\n+            ssl3_choose_cipher(s, ciphers, ssl_get_cipher_preferences(s));\n \n         if (cipher == NULL) {\n             SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE,\n@@ -1954,7 +1954,7 @@\n             /* check if some cipher was preferred by call back */\n             if (pref_cipher == NULL)\n                 pref_cipher = ssl3_choose_cipher(s, s->peer_ciphers,\n-                                                 SSL_get_ciphers(s));\n+                                                 ssl_get_cipher_preferences(s));\n             if (pref_cipher == NULL) {\n                 SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE,\n                          SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO,\n@@ -1963,8 +1963,9 @@\n             }\n \n             s->session->cipher = pref_cipher;\n-            sk_SSL_CIPHER_free(s->cipher_list);\n-            s->cipher_list = sk_SSL_CIPHER_dup(s->peer_ciphers);\n+            ssl_cipher_preference_list_free(s->cipher_list);\n+            s->cipher_list = ssl_cipher_preference_list_from_ciphers(\n+                                                       s->peer_ciphers);\n             sk_SSL_CIPHER_free(s->cipher_list_by_id);\n             s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->peer_ciphers);\n         }\n@@ -2277,7 +2278,7 @@\n             /* In TLSv1.3 we selected the ciphersuite before resumption */\n             if (!SSL_IS_TLS13(s)) {\n                 cipher =\n-                    ssl3_choose_cipher(s, s->peer_ciphers, SSL_get_ciphers(s));\n+                    ssl3_choose_cipher(s, s->peer_ciphers, ssl_get_cipher_preferences(s));\n \n                 if (cipher == NULL) {\n                     SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE,\ndiff --color -uNr a/util/libcrypto.num b/util/libcrypto.num\n--- a/util/libcrypto.num\t2022-11-01 20:36:10.000000000 +0800\n+++ b/util/libcrypto.num\t2022-12-25 15:28:59.879390122 +0800\n@@ -4591,3 +4591,4 @@\n X509_REQ_set0_signature                 4545\t1_1_1h\tEXIST::FUNCTION:\n X509_REQ_set1_signature_algo            4546\t1_1_1h\tEXIST::FUNCTION:\n EC_KEY_decoded_from_explicit_params     4547\t1_1_1h\tEXIST::FUNCTION:EC\n+EVP_chacha20_poly1305_draft             4548\t1_1_1h\tEXIST::FUNCTION:CHACHA,POLY1305\n"
  },
  {
    "path": "rimworld.mods",
    "content": "Version: 0\nMods:\n- Id: brrainz.harmony\n  Name: Harmony\n  SteamWorkshopId: 2009463077\n- Id: ludeon.rimworld\n  Name: 核心\n  SteamWorkshopId: 0\n- Id: fluffy.modmanager\n  Name: Mod Manager\n  SteamWorkshopId: 1507748539\n- Id: imranfish.xmlextensions\n  Name: XML Extensions\n  SteamWorkshopId: 2574315206\n- Id: bs.betterlog\n  Name: Better Log - Fix your errors with style\n  SteamWorkshopId: 2772559481\n- Id: ludeon.rimworld.royalty\n  Name: 皇权\n  SteamWorkshopId: 0\n- Id: ludeon.rimworld.ideology\n  Name: 文化\n  SteamWorkshopId: 0\n- Id: ludeon.rimworld.biotech\n  Name: 生物技术\n  SteamWorkshopId: 0\n- Id: unlimitedhugs.hugslib\n  Name: HugsLib\n  SteamWorkshopId: 818773962\n- Id: rwzh.chinesepack.hugslib\n  Name: HugsLib_zh\n  SteamWorkshopId: 1798770746\n- Id: superniquito.modoptionssort\n  Name: Mod Options Sort\n  SteamWorkshopId: 2910865748\n- Id: arandomkiwi.rimsaves\n  Name: RimSaves\n  SteamWorkshopId: 1713367505\n- Id: rwzh.chinesepack.rimsaves\n  Name: RimSaves_zh\n  SteamWorkshopId: 2199383843\n- Id: madeline.modmismatchformatter\n  Name: Better ModMismatch Window\n  SteamWorkshopId: 1872244972\n- Id: owlchemist.midsaversaver\n  Name: Mid-saver Saver\n  SteamWorkshopId: 2921585989\n- Id: chinesepackage.owlchemist.midsaversaver\n  Name: Mid-saver Saver_zh\n  SteamWorkshopId: 2924284353\n- Id: kenx00x.xenotypeandideologybuttonstitlescreen\n  Name: Xenotype And Ideology Buttons TitleScreen\n  SteamWorkshopId: 2973951983\n- Id: peppsen.pmusic\n  Name: P-Music\n  SteamWorkshopId: 725130005\n- Id: fluffy.musicmanager\n  Name: Music Manager\n  SteamWorkshopId: 2229205672\n- Id: telefonmast.graphicssettings\n  Name: Graphics Settings+\n  SteamWorkshopId: 1678847247\n- Id: tinygrox.chs.graphicssettings\n  Name: Graphics Settings CHS\n  SteamWorkshopId: 2888729664\n- Id: kapitanoczywisty.minimallightcontrol\n  Name: Minimal Light Control\n  SteamWorkshopId: 1554307259\n- Id: oskarpotocki.vanillafactionsexpanded.core\n  Name: Vanilla Expanded Framework\n  SteamWorkshopId: 2023507013\n- Id: rwzh.chinesepack.vanillafactionsexpandedcore\n  Name: Vanilla_Expanded_Framework_zh\n  SteamWorkshopId: 2024971883\n- Id: vanillaexpanded.vanillatraitsexpanded\n  Name: Vanilla Traits Expanded\n  SteamWorkshopId: 2296404655\n- Id: rwzh.chinesepack.vanillatraitsexpanded\n  Name: Vanilla_Traits_Expanded_zh\n  SteamWorkshopId: 2301508015\n- Id: vanillaexpanded.vtexe\n  Name: Vanilla Textures Expanded\n  SteamWorkshopId: 2016436324\n- Id: rwzh.chinesepack.vtexe\n  Name: Vanilla_Textures_Expanded_zh\n  SteamWorkshopId: 2035866176\n- Id: vanillaexpanded.vfepower\n  Name: Vanilla Furniture Expanded - Power\n  SteamWorkshopId: 2062943477\n- Id: rwzh.chinesepack.vfe\n  Name: Vanilla_Furniture_Expanded_zh_pack\n  SteamWorkshopId: 2033672219\n- Id: vanillaexpanded.vcef\n  Name: Vanilla Fishing Expanded\n  SteamWorkshopId: 1914064942\n- Id: rwzh.chinesepack.vcef\n  Name: Vanilla_Fishing_Expanded_zh\n  SteamWorkshopId: 2035866747\n- Id: tenthwit.apparel\n  Name: Retextured Apparel\n  SteamWorkshopId: 2042613316\n- Id: tied.parkaretex\n  Name: Parka Retexture\n  SteamWorkshopId: 2984307648\n- Id: tenthwit.sculptures\n  Name: Retextured Sculptures\n  SteamWorkshopId: 2045252395\n- Id: owlchemist.bettervanillamasking\n  Name: Better Vanilla Masking\n  SteamWorkshopId: 1736114368\n- Id: unofficial.morepracticaltraits\n  Name: More Practical Traits - unofficial update\n  SteamWorkshopId: 2893524668\n- Id: moretraitslots.kv.rw\n  Name: '[KV] More Trait Slots [1.4]'\n  SteamWorkshopId: 2878346573\n- Id: zh.moretraitslotstaranchuk.only\n  Name: '[KV] More Trait Slots [1.4] -- 更多特性槽1.4 简体汉化包'\n  SteamWorkshopId: 2878778439\n- Id: darkborderman.moretraitgenes\n  Name: More Genes\n  SteamWorkshopId: 2878539467\n- Id: zh.moretraitgenes\n  Name: More Genes简体中文汉化包\n  SteamWorkshopId: 2884401339\n- Id: gloomy.face.mk2.unofficial\n  Name: Gloomy Face mk2 Unofficial\n  SteamWorkshopId: 2910048077\n- Id: nals.chibifacefemale\n  Name: '[NL] Chibi Face Female only'\n  SteamWorkshopId: 1540475285\n- Id: hourai.vanillahairsretex\n  Name: Vanilla Hairs Retextured\n  SteamWorkshopId: 2893572291\n- Id: klr.gloomyhairmk2forked\n  Name: Gloomy Hair mk2 FORKED[1.4]\n  SteamWorkshopId: 2895002564\n- Id: jintuzilamian.hairstyle.afumenshairstyles\n  Name: AFU男士发型_Men's hairstyles\n  SteamWorkshopId: 2072106893\n- Id: jintuzilamian.hairstyle.afuwomenshairstyles\n  Name: AFU女士发型_Women's hairstyles\n  SteamWorkshopId: 2020779666\n- Id: nera.spawnthosegenes\n  Name: '[BT] SpawnThoseGenes!'\n  SteamWorkshopId: 2898044088\n- Id: nals.facialanimation\n  Name: '[NL] Facial Animation - WIP'\n  SteamWorkshopId: 1635901197\n- Id: oppey.eyegenes2\n  Name: '[BT] EyeGenes2 | Base - [NL] Facial Animation |'\n  SteamWorkshopId: 2898151329\n- Id: oppey.goawayeyestrain\n  Name: Go Away Eye Strain!\n  SteamWorkshopId: 2895295315\n- Id: atom.tree\n  Name: Anima and Gauranlen Tree Retexture\n  SteamWorkshopId: 2662326639\n- Id: agentblac.makepawnsprisoners\n  Name: 1.4 - Imprisonment On The Go! (Make Pawns Prisoners Without Beds)\n  SteamWorkshopId: 1203903378\n- Id: wrk.woundremover\n  Name: 1.3/Ideology Wound Remover\n  SteamWorkshopId: 2839444849\n- Id: edb.preparecarefully\n  Name: EdB Prepare Carefully\n  SteamWorkshopId: 735106432\n- Id: void.charactereditor\n  Name: Character Editor\n  SteamWorkshopId: 1874644848\n- Id: boundir.newgameplus\n  Name: New Game Plus\n  SteamWorkshopId: 2909126210\n- Id: zh.boundir.newgameplus\n  Name: New Game Plus_zh\n  SteamWorkshopId: 2921894258\n- Id: sarg.smartspeed\n  Name: Smart Speed\n  SteamWorkshopId: 1504723424\n- Id: zylle.mapdesigner\n  Name: Map Designer\n  SteamWorkshopId: 2111424996\n- Id: rwzh.chinesepack.mapdesigner\n  Name: Map_Designer_zh\n  SteamWorkshopId: 2115722898\n- Id: m00nl1ght.mappreview\n  Name: Map Preview\n  SteamWorkshopId: 2800857642\n- Id: zh.m00nl1ght.mappreview\n  Name: Map Preview_zh\n  SteamWorkshopId: 2804421885\n- Id: m00nl1ght.geologicallandforms\n  Name: Geological Landforms\n  SteamWorkshopId: 2773943594\n- Id: zh.m00nl1ght.geologicallandforms\n  Name: Geological Landforms_zh\n  SteamWorkshopId: 2774968010\n- Id: thereallemon.mapreroll\n  Name: Map Reroll (1.4)\n  SteamWorkshopId: 2915575236\n- Id: mlie.savemaps\n  Name: Save Maps (Continued)\n  SteamWorkshopId: 2916523481\n- Id: murmur.threatpointcap\n  Name: Threat Point Cap\n  SteamWorkshopId: 2380491067\n- Id: dark.jobinbar\n  Name: Job In Bar\n  SteamWorkshopId: 2086300611\n- Id: chinesetranslation.whw.jobinbar\n  Name: Job In Bar 个人简中汉化\n  SteamWorkshopId: 2625438810\n- Id: derekbickley.ltocolonygroupsfinal\n  Name: '[LTO] Colony Groups'\n  SteamWorkshopId: 2345493945\n- Id: zh.ltocolonygroupsfinal\n  Name: '[LTO] Colony Groups简繁中文汉化包'\n  SteamWorkshopId: 2364514563\n- Id: dubwise.dubsmintminimap\n  Name: Dubs Mint Minimap\n  SteamWorkshopId: 1662119905\n- Id: owlchemist.powertab\n  Name: Power Tab 2\n  SteamWorkshopId: 2952716728\n- Id: hanami.chinesepack.owlchemist.powertab\n  Name: Power Tab 2个人汉化\n  SteamWorkshopId: 2972581353\n- Id: jaxe.bubbles\n  Name: Interaction Bubbles\n  SteamWorkshopId: 1516158345\n- Id: jaxe.bubbles.zh.hc\n  Name: Interaction Bubbles_zh\n  SteamWorkshopId: 2707586104\n- Id: jaxe.rimhud\n  Name: RimHUD\n  SteamWorkshopId: 1508850027\n- Id: shouer.chinesepack.rimhud\n  Name: RimHUD_zh\n  SteamWorkshopId: 1928161832\n- Id: creeper.betterinfocard\n  Name: BetterInfoCard\n  SteamWorkshopId: 2890920739\n- Id: kahdeg.killfeed\n  Name: Killfeed\n  SteamWorkshopId: 1362098265\n- Id: dubwise.dubsappareltweaks\n  Name: Dubs Apparel Tweaks\n  SteamWorkshopId: 2296697286\n- Id: rwzh.leafzxg.dubsappareltweaks\n  Name: Dubs_Apparel_Tweaks_zh\n  SteamWorkshopId: 2327363931\n- Id: avilmask.commonsense\n  Name: Common Sense\n  SteamWorkshopId: 1561769193\n- Id: dingo.lessrebuff\n  Name: Less Rebuff\n  SteamWorkshopId: 774543761\n- Id: neronix17.tweaksgalore\n  Name: Tweaks Galore\n  SteamWorkshopId: 2695164414\n- Id: mobius.royaltytweaks\n  Name: Royalty Tweaks\n  SteamWorkshopId: 2018384424\n- Id: insertkey.buildertweaks\n  Name: BuilderTweaks\n  SteamWorkshopId: 1600554794\n- Id: enterprise.changeoperationsuccessratelimit\n  Name: 修改手术成功率上限-Change Operation Success Rate Limit\n  SteamWorkshopId: 2927674331\n- Id: brrainz.justignoremepassing\n  Name: Just Ignore Me Passing\n  SteamWorkshopId: 1231617602\n- Id: doll.nevergeneraterelations\n  Name: Never Generate Relations\n  SteamWorkshopId: 2891797130\n- Id: mlie.allmemoriesfade\n  Name: All Memories Fade\n  SteamWorkshopId: 2800155563\n- Id: wemd.handsarenotoutside\n  Name: '[WD] Hands Are Not Outside'\n  SteamWorkshopId: 1985331019\n- Id: vesper.notmyfault\n  Name: Not My Fault\n  SteamWorkshopId: 2870045856\n- Id: forevermarried.mod\n  Name: \"'Til Death Do Them Apart\"\n  SteamWorkshopId: 2975068661\n- Id: cedaro.nohopelessromance\n  Name: No Hopeless Romance\n  SteamWorkshopId: 2964282061\n- Id: amch.needbaroverflow\n  Name: Need Bar Overflow\n  SteamWorkshopId: 2566316158\n- Id: bustedbunny.chooseyourrecipe\n  Name: Choose Your Recipe\n  SteamWorkshopId: 2613112847\n- Id: dubwise.dubsmintmenus\n  Name: Dubs Mint Menus\n  SteamWorkshopId: 1446523594\n- Id: rwzh.chinesepack.dubsmintmenus\n  Name: Dubs_Mint_Menus_zh\n  SteamWorkshopId: 2327364313\n- Id: mlie.configurableroomstats\n  Name: Configurable Room Stats (Continued)\n  SteamWorkshopId: 2896467431\n- Id: owlchemist.toggleableoverlays\n  Name: Toggleable Overlays\n  SteamWorkshopId: 2608654598\n- Id: owlchemist.toggleablereadouts\n  Name: Toggleable Readouts\n  SteamWorkshopId: 2661792499\n- Id: zh.owlchemist.toggleableseries\n  Name: Owlchemist - Toggleable Series_zh\n  SteamWorkshopId: 2768513554\n- Id: syrus.heatmap\n  Name: Heat Map (Continued)\n  SteamWorkshopId: 2552838384\n- Id: legodude17.qualcolor\n  Name: Quality Colors\n  SteamWorkshopId: 2420141361\n- Id: qualcolor.zh\n  Name: Quality Colors zh 简体中文汉化\n  SteamWorkshopId: 2891521968\n- Id: owlchemist.perspectiveores\n  Name: 'Perspective: Ores'\n  SteamWorkshopId: 2923871543\n- Id: owlchemist.simplefx.vapor\n  Name: 'Simple FX: Vapor'\n  SteamWorkshopId: 2886571275\n- Id: owlchemist.simplefx.splashes\n  Name: 'Simple FX: Splashes'\n  SteamWorkshopId: 2889330414\n- Id: owlchemist.simplefx.smoke2\n  Name: 'Simple FX: Smoke'\n  SteamWorkshopId: 2574489704\n- Id: owlchemist.scatteredflames\n  Name: Scattered Flames\n  SteamWorkshopId: 2652749788\n- Id: owlchemist.scatteredflames.zh.hc\n  Name: Scattered Flames_zh\n  SteamWorkshopId: 2711236044\n- Id: telardo.fireworks\n  Name: Fireworks\n  SteamWorkshopId: 2922179297\n- Id: fishbones.daylilyretexture\n  Name: I Hate Daylilies\n  SteamWorkshopId: 2878142283\n- Id: smashphil.loadinginprogress\n  Name: Loading In Progress\n  SteamWorkshopId: 1975622772\n- Id: wiseclock.ac.daysmatter\n  Name: Days Matter (fork)\n  SteamWorkshopId: 2045496988\n- Id: telardo.romanceontherim\n  Name: Romance On The Rim\n  SteamWorkshopId: 2654432921\n- Id: vanya.tools.blightedalert\n  Name: Blighted Alert\n  SteamWorkshopId: 1494937292\n- Id: fyarn.fixablemooddebuffsalert\n  Name: Fixable Mood Debuffs Alert\n  SteamWorkshopId: 1307316672\n- Id: tammybee.predatorhuntalert\n  Name: Predator Hunt Alert\n  SteamWorkshopId: 1537786185\n- Id: kathanon.repairinthezone\n  Name: Repair in the Zone\n  SteamWorkshopId: 2798537004\n- Id: jp.adjacentlight\n  Name: Adjacent Light\n  SteamWorkshopId: 2517853803\n- Id: leoltron.maxbuymaxsell\n  Name: Max Buy, Max Sell\n  SteamWorkshopId: 2889812094\n- Id: meow.tradablestoneblocks\n  Name: Tradable Stone Blocks\n  SteamWorkshopId: 1522257424\n- Id: tac.genetrader\n  Name: Gene Trader\n  SteamWorkshopId: 2886375137\n- Id: timmyliang.tradehelper\n  Name: TradeHelper\n  SteamWorkshopId: 2113372560\n- Id: grizzlemethis.tradingspot.rw\n  Name: '[GMT] Trading Spot'\n  SteamWorkshopId: 2874517333\n- Id: smashphil.dropspot\n  Name: Trade Ships Drop Spot\n  SteamWorkshopId: 1969732297\n- Id: joseasoler.tradingoptions\n  Name: Trading Options\n  SteamWorkshopId: 2876541977\n- Id: zh.joseasoler.tradingoptions\n  Name: Trading Options-汉化\n  SteamWorkshopId: 2876724391\n- Id: mlie.dismisstrader\n  Name: Dismiss Trader (Continued)\n  SteamWorkshopId: 2078518511\n- Id: zephyr.dismisstraderzh\n  Name: Dismiss Trader (Continued)简繁汉化\n  SteamWorkshopId: 2081531370\n- Id: torann.caravanoptions\n  Name: CaravanOptions\n  SteamWorkshopId: 1501729394\n- Id: torann.caravanoptions.zh\n  Name: CaravanOptions 简体中文汉化 远行队选项\n  SteamWorkshopId: 2722022165\n- Id: hatena.animaldiscovery\n  Name: Animal Discovery\n  SteamWorkshopId: 2172166576\n- Id: merthsoft.designatorshapes\n  Name: Designator Shapes\n  SteamWorkshopId: 1235181370\n- Id: ogliss.wanderjoinsplus\n  Name: WanderJoinsPlus\n  SteamWorkshopId: 2177016016\n- Id: ogliss.rescueejoinsplus\n  Name: RescueeJoinsPlus\n  SteamWorkshopId: 2177170403\n- Id: mlie.incidentpersonstat\n  Name: Incident Person Stat (Continued)\n  SteamWorkshopId: 2896052868\n- Id: brrainz.cameraplus\n  Name: Camera+\n  SteamWorkshopId: 867467808\n- Id: rwzh.chinesepack.cameraplus\n  Name: Camera+_zh\n  SteamWorkshopId: 1783453942\n- Id: fluffy.followme\n  Name: Follow Me\n  SteamWorkshopId: 715759739\n- Id: com.github.alandariva.moreplanning\n  Name: More Planning [1.4]\n  SteamWorkshopId: 2551225702\n- Id: unlimitedhugs.allowtool\n  Name: Allow Tool\n  SteamWorkshopId: 761421485\n- Id: unlimitedhugs.defensivepositions\n  Name: Defensive Positions\n  SteamWorkshopId: 761219125\n- Id: rwzh.chinesepack.unlimitedhugs\n  Name: UnlimitedHugs_zh_pack\n  SteamWorkshopId: 2016725307\n- Id: fluffy.blueprints\n  Name: Blueprints\n  SteamWorkshopId: 708455313\n- Id: rwzh.leafzxg.blueprints\n  Name: Blueprints_zh\n  SteamWorkshopId: 2905109743\n- Id: navyseal5.moreharvestdesignators\n  Name: More Harvest Designators!\n  SteamWorkshopId: 1541250497\n- Id: thewirelord354.conduitdeconstruct\n  Name: Conduit Deconstruct\n  SteamWorkshopId: 838336462\n- Id: leafzxg.masterofcooking\n  Name: Master of Cooking\n  SteamWorkshopId: 830545304\n- Id: leafzxg.masterofcrafting\n  Name: Master of Crafting\n  SteamWorkshopId: 848513545\n- Id: mlie.foodpoisoningstackfix\n  Name: Food Poisoning Stack Fix (Continued)\n  SteamWorkshopId: 2843483188\n- Id: frozensnowfox.filthvanisheswithrainandtime\n  Name: '[FSF] Filth Vanishes With Rain And Time'\n  SteamWorkshopId: 1508341791\n- Id: owlchemist.permeableterrain\n  Name: Permeable Terrain\n  SteamWorkshopId: 2629981384\n- Id: oninotamasi.owlchemist.permeableterrain.zh\n  Name: Permeable Terrain繁簡漢化\n  SteamWorkshopId: 2944952044\n- Id: mario1.3patch.metaldontburn\n  Name: Metal Don't Burn (UNOFFICIAL 1.4 PATCH)\n  SteamWorkshopId: 2557145474\n- Id: chicken305.efficienlight\n  Name: Chickens Efficient Light\n  SteamWorkshopId: 1498059716\n- Id: telardo.dragselect\n  Name: DragSelect\n  SteamWorkshopId: 2599942235\n- Id: haecriver.injuredcarry\n  Name: Injured Carry\n  SteamWorkshopId: 2413690575\n- Id: alien.milkablemuffalo\n  Name: Milkable Muffalo\n  SteamWorkshopId: 2019852283\n- Id: automatic.prisonerbedsetowner\n  Name: Set Owner for Prisoner Beds\n  SteamWorkshopId: 2053931388\n- Id: kathanon.dropsome\n  Name: Drop Some\n  SteamWorkshopId: 2823342374\n- Id: mlgm.dropsome.zh\n  Name: Drop Some zh 丢弃数量可选\n  SteamWorkshopId: 2974880246\n- Id: kb.dropinventory\n  Name: Drop All Inventory\n  SteamWorkshopId: 2882490442\n- Id: mlgm.dropinventory.zh\n  Name: Drop All Inventory_zh一键卸载库存\n  SteamWorkshopId: 2974881350\n- Id: velc.itemlist\n  Name: Pawn's Items List\n  SteamWorkshopId: 1507341718\n- Id: owlchemist.doorclearance\n  Name: Door Clearance\n  SteamWorkshopId: 2965544918\n- Id: mlie.nomoreautoroof\n  Name: NoMoreAutoroof (Continued)\n  SteamWorkshopId: 2026621806\n- Id: legodude17.smartdecon\n  Name: Smarter Deconstruction and Mining\n  SteamWorkshopId: 2398365712\n- Id: legodude17.smartdecon.zh\n  Name: Smarter Deconstruction and Mining zh 简体中文\n  SteamWorkshopId: 2891998257\n- Id: dhultgren.smarterconstruction\n  Name: Smarter Construction\n  SteamWorkshopId: 2202185773\n- Id: tammybee.cutplantsbeforebuilding\n  Name: Cut plants before building\n  SteamWorkshopId: 1539025677\n- Id: fluffy.medicaltab\n  Name: Medical Tab\n  SteamWorkshopId: 715565817\n- Id: rwzh.leafzxg.medicaltab\n  Name: Medical_Tab_zh\n  SteamWorkshopId: 2905108685\n- Id: fluffy.worktab\n  Name: Work Tab\n  SteamWorkshopId: 725219116\n- Id: rwzh.leafzxg.worktab\n  Name: Work_Tab_zh\n  SteamWorkshopId: 2905108789\n- Id: voult.betterpawncontrol\n  Name: Better Pawn Control\n  SteamWorkshopId: 1541460369\n- Id: jp.perfectpathing\n  Name: Perfect Pathfinding\n  SteamWorkshopId: 2341486509\n- Id: telardo.nolaggybed\n  Name: No Laggy Bed\n  SteamWorkshopId: 2790250834\n- Id: uuugggg.sharetheload\n  Name: Share The Load\n  SteamWorkshopId: 1356838246\n- Id: uuugggg.sharetheload.zh\n  Name: Share The Load zh 简体中文补丁\n  SteamWorkshopId: 2892578913\n- Id: owlchemist.cleanpathfinding\n  Name: Clean Pathfinding 2\n  SteamWorkshopId: 2603765747\n- Id: oninotamasi.owlchemist.cleanpathfinding.zh\n  Name: Clean Pathfinding 2繁簡漢化\n  SteamWorkshopId: 2947353787\n- Id: victor.wallsaresolid\n  Name: Walls are solid\n  SteamWorkshopId: 2896548513\n- Id: aelanna.cleaningspeed\n  Name: Cleaning Speed\n  SteamWorkshopId: 2748222814\n- Id: zh.aelanna.cleaningspeed\n  Name: Cleaning Speed-汉化\n  SteamWorkshopId: 2748802839\n- Id: seekiworksmod.no11\n  Name: Chop Wood with Other Skills\n  SteamWorkshopId: 2811154414\n- Id: puremj.mjrimmods.vanillafixmortarshellloading\n  Name: 'Vanilla Fix: Mortar Shell Loading'\n  SteamWorkshopId: 2799902757\n- Id: puremj.mjrimmods.vanillafixhaulafterslaughter\n  Name: 'Vanilla Fix: Haul After Slaughter'\n  SteamWorkshopId: 2801452324\n- Id: puremj.mjrimmods.whileyouarenearby\n  Name: While You Are Nearby\n  SteamWorkshopId: 2784585275\n- Id: codeoptimist.jobsofopportunity\n  Name: While You're Up / PUAH+\n  SteamWorkshopId: 2034960453\n- Id: rwzh.chinesepack.jobsofopportunity\n  Name: While_You're_U_PUAH+_zh\n  SteamWorkshopId: 2039718734\n- Id: mlie.pawneducation\n  Name: Pawn Education (Continued)\n  SteamWorkshopId: 2296533470\n- Id: rwzh.chinesepack.pawneducation\n  Name: Pawn_Education_zh\n  SteamWorkshopId: 2380908925\n- Id: fed1splay.pawntargetfix\n  Name: PawnTargetFix\n  SteamWorkshopId: 2014789938\n- Id: brrainz.achtung\n  Name: Achtung!\n  SteamWorkshopId: 730936602\n- Id: rwzh.chinesepack.achtung\n  Name: Achtung!_zh\n  SteamWorkshopId: 1782765741\n- Id: rh2.bcds.first.aid\n  Name: '[RH2] BCD: First Aid'\n  SteamWorkshopId: 2563152474\n- Id: zh.rh2bcdfirstaid\n  Name: '[RH2]BCD:First Aid_zh'\n  SteamWorkshopId: 2566595634\n- Id: rh2.cpers.arrest.here\n  Name: '[RH2] CPERS: Arrest Here!'\n  SteamWorkshopId: 2563157350\n- Id: zh.rh2cpersarresthere\n  Name: '[RH2]CPERS: Arrest Here!_zh'\n  SteamWorkshopId: 2566596115\n- Id: roolo.searchanddestroy\n  Name: Search and Destroy\n  SteamWorkshopId: 1467764609\n- Id: mlie.dynamiccarryingcapacity\n  Name: Dynamic Carrying Capacity (Continued)\n  SteamWorkshopId: 2967228692\n- Id: vanya.tools.bulkcarrier\n  Name: Bulk Carrier\n  SteamWorkshopId: 1428989232\n- Id: mlie.10xcarryingcapacity\n  Name: 10x Carrying Capacity (Continued)\n  SteamWorkshopId: 2019000693\n- Id: murmur.isid\n  Name: Infestations Spawn in Darkness\n  SteamWorkshopId: 2017514331\n- Id: murmur.fasterprojectiles\n  Name: Faster Projectiles\n  SteamWorkshopId: 2880014259\n- Id: wemd.whitepowerarmor\n  Name: '[WD] White Power Armor'\n  SteamWorkshopId: 1122453564\n- Id: crocodil.foodpoisoningcures\n  Name: Food Poisoning Cures\n  SteamWorkshopId: 2856335335\n- Id: zh.crocodil.foodpoisoningcures\n  Name: Food Poisoning Cures_zh\n  SteamWorkshopId: 2857544882\n- Id: eagle0600.dresspatients.1.4\n  Name: Dress Patients (1.4)\n  SteamWorkshopId: 2877763074\n- Id: rwzh.chinesepack.dresspatients\n  Name: Dress_Patients_zh\n  SteamWorkshopId: 2063186733\n- Id: kathanon.followthevein\n  Name: Follow the Vein\n  SteamWorkshopId: 2923356196\n- Id: gorymoon.closeall\n  Name: CloseAll\n  SteamWorkshopId: 2018479759\n- Id: dani.dismissletters\n  Name: '[DN] Auto Dismiss Letters'\n  SteamWorkshopId: 2830610278\n- Id: sgc.quests\n  Name: Quest Reward Rebalance\n  SteamWorkshopId: 2312419897\n- Id: sandy.rpgstyleinventory.avilmask.revamped\n  Name: RPG Style Inventory Revamped\n  SteamWorkshopId: 2478833213\n- Id: lbmaian.begonemessage\n  Name: Begone, Message!\n  SteamWorkshopId: 1951637504\n- Id: kathanon.showweapontallies\n  Name: Show Weapon Tallies\n  SteamWorkshopId: 2901520677\n- Id: dakkpasserida.animareplant\n  Name: Replantable Anima Trees\n  SteamWorkshopId: 2586765823\n- Id: proxyer.anestheticpatch\n  Name: Anesthetic balance patch\n  SteamWorkshopId: 2038485179\n- Id: proxyer.steamgeyserbuilding\n  Name: Geyser Deconstruct patch\n  SteamWorkshopId: 2317497710\n- Id: ben.automaticnightowl\n  Name: Automatic Night Owl\n  SteamWorkshopId: 2056012179\n- Id: dninemfive.qualitycooldown\n  Name: Quality Cooldown\n  SteamWorkshopId: 1543069597\n- Id: gm.headsetwhelmet\n  Name: Mechanitor Headset with Helmet\n  SteamWorkshopId: 2881642669\n- Id: wvc.sergkart.biotech.mechanoididleoptimization\n  Name: Mechanoid Idle Optimization\n  SteamWorkshopId: 2885792215\n- Id: scherub.stonecuttingextended\n  Name: Stonecutting Extended\n  SteamWorkshopId: 2571676542\n- Id: lanking.syntheticchips\n  Name: Lanking's Synthetic Chips\n  SteamWorkshopId: 2971197280\n- Id: winds.bettermechanoidloot\n  Name: Better Mechanoid Loot (重写)\n  SteamWorkshopId: 2963175073\n- Id: kikohi.bettergroundpenetratingscanner\n  Name: Better ground-penetrating scanner\n  SteamWorkshopId: 2809972387\n- Id: zh.kikohi.bettergroundpenetratingscanner\n  Name: Better ground-penetrating scanner_zh\n  SteamWorkshopId: 2810420557\n- Id: victor.psyultra\n  Name: Psy Ultra\n  SteamWorkshopId: 2879578025\n- Id: xrushha.craftablebroadshieldcore\n  Name: 'Craftable uncraftables: Shield Core'\n  SteamWorkshopId: 2198480490\n- Id: coldzero.craftableroyaltyweapons\n  Name: Craftable Royalty Weapons (Continued)\n  SteamWorkshopId: 2559489638\n- Id: zephyr.craftableroyaltyweaponszh\n  Name: Craftable Royalty Weapons 皇权武器制造简繁汉化\n  SteamWorkshopId: 2063798513\n- Id: hrg164hjo.steriletilepatch\n  Name: Make sterile tile beautiful again\n  SteamWorkshopId: 2895016825\n- Id: cuddle.donottakemyguns\n  Name: Don't take my guns\n  SteamWorkshopId: 1869879952\n- Id: owlchemist.undergroundpowerconduits\n  Name: Underground Power Conduits\n  SteamWorkshopId: 1735421319\n- Id: red.owlchemist.undergroundpowerconduits.zh\n  Name: Underground Power Conduits 简繁汉化包\n  SteamWorkshopId: 2744717109\n- Id: leafzxg.deerextension\n  Name: Deer Extension\n  SteamWorkshopId: 1336473924\n- Id: wemd.fastmoisturepumps\n  Name: '[WD] Fast Moisture Pumps'\n  SteamWorkshopId: 1164753788\n- Id: murmur.walllight\n  Name: Wall Light\n  SteamWorkshopId: 1423699208\n- Id: zh.walllight.only\n  Name: Wall Light -- 壁灯 简体汉化包\n  SteamWorkshopId: 2826726307\n- Id: gt.sam.tilledsoil\n  Name: Tilled Soil\n  SteamWorkshopId: 725747149\n- Id: rimfridge.kv.rw\n  Name: RimFridge Updated\n  SteamWorkshopId: 2878183338\n- Id: haplo.miscellaneous.training\n  Name: Misc. Training\n  SteamWorkshopId: 717575199\n- Id: fluffy.backuppower\n  Name: Backup Power\n  SteamWorkshopId: 2084493662\n- Id: dracoix.doormat.r12a\n  Name: Door Mat\n  SteamWorkshopId: 1505423207\n- Id: notfood.mendandrecycle\n  Name: MendAndRecycle\n  SteamWorkshopId: 735241897\n- Id: vis.staticquality\n  Name: Static Quality\n  SteamWorkshopId: 2801204005\n- Id: zh.vis.staticquality\n  Name: Static Quality_zh\n  SteamWorkshopId: 2907151281\n- Id: ratys.madskills\n  Name: Mad Skills\n  SteamWorkshopId: 731111514\n- Id: hatti.qualitybuilder\n  Name: QualityBuilder\n  SteamWorkshopId: 754637870\n- Id: rwzh.chinesepack.qualitybuilder\n  Name: QualityBuilder_zh\n  SteamWorkshopId: 1783553525\n- Id: jgh.qualitybuilder\n  Name: QualityBuilder retexture\n  SteamWorkshopId: 2638388641\n- Id: vat.epoeforked\n  Name: Expanded Prosthetics and Organ Engineering - Forked\n  SteamWorkshopId: 1949064302\n- Id: rwzh.leafzxg.epoeforked\n  Name: Expanded_Prosthetics_and_Organ_Engineering_Forked_zh\n  SteamWorkshopId: 2021158028\n- Id: vat.epoeforkedroyalty\n  Name: 'EPOE-Forked: Royalty DLC expansion'\n  SteamWorkshopId: 2008970276\n- Id: rwzh.leafzxg.epoeforkedroyalty\n  Name: EPOE-Forked_Royalty_DLC_expansion_zh\n  SteamWorkshopId: 2026231230\n- Id: nomadrw.turretvanillaretexturerestyle\n  Name: Turret Vanilla Retexture and Restyle\n  SteamWorkshopId: 2980467701\n- Id: mosi.bridgecolor2\n  Name: 'Bridge Color Tweak 2: Colorful Boogaloo'\n  SteamWorkshopId: 2985505036\n- Id: iexist.biotech.morebandwidth\n  Name: Bandwidth Enhancer\n  SteamWorkshopId: 2888505789\n- Id: zh.iexist.biotech.morebandwidth\n  Name: Bandwidth Enhancer-汉化\n  SteamWorkshopId: 2889649472\n- Id: inglix.fasterbiosculptingpod\n  Name: Faster Biosculpter Pod\n  SteamWorkshopId: 2576257954\n- Id: inglix.fasterbiosculptingpod.zh\n  Name: Faster Biosculpter Pod 简繁中文汉化 更快的塑型仓\n  SteamWorkshopId: 2745481476\n- Id: waether.biosculptingplus\n  Name: BioSculptingPlus\n  SteamWorkshopId: 2577455618\n- Id: pzqaks.biosculptingplus.zh\n  Name: BioSculptingPlus 简繁汉化包\n  SteamWorkshopId: 2619378452\n- Id: balistafreak.standalonehotspring\n  Name: Standalone Hot Spring\n  SteamWorkshopId: 2205980094\n- Id: rwzh.chinesepack.standalonehotspring\n  Name: Standalone_Hot_Spring_zh\n  SteamWorkshopId: 2210994825\n- Id: vanya.agent.housemaidnukos\n  Name: '[L] House Maid Nukos'\n  SteamWorkshopId: 1418683071\n- Id: mlie.prisonersshouldfearturrets\n  Name: Prisoners Should Fear Turrets\n  SteamWorkshopId: 2602436826\n- Id: bustedbunny.prisonersarenotswines\n  Name: Prisoners Are Not Swines\n  SteamWorkshopId: 2613966365\n- Id: jelly.propershotguns\n  Name: '[XND] Proper Shotguns (Continued)'\n  SteamWorkshopId: 2584374906\n- Id: propershotguns.llamapatch\n  Name: Llama's Proper Shotgun Patches\n  SteamWorkshopId: 2909790590\n- Id: hobtook.mortaraccuracy\n  Name: Mortar Accuracy\n  SteamWorkshopId: 1729446857\n- Id: essecoisa.flooredyasto\n  Name: Floored - Yellow and Striped Tiles Only [1.0+]\n  SteamWorkshopId: 1543195051\n- Id: doctorstupid.sofa\n  Name: Literally just a sofa\n  SteamWorkshopId: 2894560149\n- Id: fluffy.fluffybreakdowns\n  Name: Fluffy Breakdowns\n  SteamWorkshopId: 726244033\n- Id: mlie.enterhere\n  Name: Enter Here\n  SteamWorkshopId: 2824117092\n- Id: zh.enterhere.only\n  Name: Enter Here -- 客从此进 简繁汉化包\n  SteamWorkshopId: 2826779697\n- Id: bbbbilly.makereinforcedbarrel\n  Name: Make Reinforced Barrel\n  SteamWorkshopId: 2538773133\n- Id: frozensnowfox.nodefaultshelfstorage\n  Name: '[FSF] No Default Shelf Storage'\n  SteamWorkshopId: 945085502\n- Id: nomaxbills.kv.rw\n  Name: '[KV] No Max Bills'\n  SteamWorkshopId: 1588831229\n- Id: falconne.bwm\n  Name: Better Workbench Management\n  SteamWorkshopId: 935982361\n- Id: maarx.assignedworkbenches\n  Name: AssignedWorkbenches\n  SteamWorkshopId: 2973942644\n- Id: neronix17.embrasures\n  Name: Seamless Embrasures\n  SteamWorkshopId: 2290689449\n- Id: darthsergeant.10turrets\n  Name: 1.0 Turrets\n  SteamWorkshopId: 2028662113\n- Id: neceros.dws.simpleturrets\n  Name: Simple Turrets\n  SteamWorkshopId: 2559263455\n- Id: witek.mechscanhaultocharger\n  Name: Mechs can haul to charger\n  SteamWorkshopId: 2883113037\n- Id: porio.tunnulerfix\n  Name: Allow Tunnelers To Drill\n  SteamWorkshopId: 2882915775\n- Id: witek.fabricorscanrepairmechs\n  Name: Fabricors can repair mechs\n  SteamWorkshopId: 2886185474\n- Id: overpl.drawnspots\n  Name: Drawn Spots\n  SteamWorkshopId: 2917791361\n- Id: ratys.rtfuse\n  Name: RT Fuse\n  SteamWorkshopId: 728314182\n- Id: ratys.rtsolarflareshield\n  Name: RT Solar Flare Shield\n  SteamWorkshopId: 728315620\n- Id: rwzh.chinesepack.ratys\n  Name: RT_zh_pack\n  SteamWorkshopId: 2015185500\n- Id: stormknight.outdoorlighting\n  Name: Outdoor Lighting\n  SteamWorkshopId: 1545708330\n- Id: chen.outdoorlighting.zh\n  Name: Outdoor Lighting-zh\n  SteamWorkshopId: 2902036198\n- Id: gwinnbleidd.mothballedanddeadpawns\n  Name: 'Better GC: Mothballed and World Pawns'\n  SteamWorkshopId: 2982026860\n- Id: trinity.runtimegcfixed\n  Name: RuntimeGC [1.4]\n  SteamWorkshopId: 2983911897\n- Id: taranchuk.performanceoptimizer\n  Name: Performance Optimizer\n  SteamWorkshopId: 2664723367\n- Id: krkr.rocketman\n  Name: RocketMan - Performance Mod\n  SteamWorkshopId: 2479389928\n- Id: dubwise.dubsperformanceanalyzer.steam\n  Name: Dubs Performance Analyzer\n  SteamWorkshopId: 2038874626\n"
  },
  {
    "path": "seewo.fw",
    "content": "seewo.com\ncvte.com\ncvtestatic.com\ncvtapi.com\nr302.cc\n\njpush.cn\numeng.com\nifconfig.me\n\nqbox.me\nqiniu.com\nqiniup.com\nqiniu.com.w.kunlunno.com\naliyuncs.com\n\nen5static.com\njikstatic.com\nxbstatic.com\nbystatic.com\nseedpace.com\n\nup.qbox.me\nupload.qiniup.com\nupload.qiniu.com\ntinychinacdnweb.qiniu.com.w.kunlunno.com\n\nqn-pri-easynote-new.en5static.com\nali-pro-pub.jikstatic.com\nali-pro-pri.xbstatic.com\ncos-pro-pub.bystatic.com\nalert-push.seedpace.com\ncos-pro-pri.xbstatic.com\nali-pro-pri-auth.xbstatic.com\n\nali-backup-pri.oss-cn-hangzhou.aliyuncs.com\nali-backup-pub.oss-cn-hangzhou.aliyuncs.com\ncstore-prot-pub.oss-cn-hangzhou.aliyuncs.com\ncstore-hls-pub.oss-cn-hangzhou.aliyuncs.com\ncstore-hls-pri.oss-cn-hangzhou.aliyuncs.com\ncstore-public.oss-cn-hangzhou.aliyuncs.com\ncstore-public.oss-cn-hangzhou-internal.aliyuncs.com\ncstore-private.oss-cn-hangzhou.aliyuncs.com\ncstore-private.oss-cn-hangzhou-internal.aliyuncs.com\ntmp-aliyun-pri.oss-cn-hangzhou.aliyuncs.com\ntmp-aliyun-pub.oss-cn-hangzhou.aliyuncs.com\ntmp-aliyun-pub.oss-cn-hangzhou-internal.aliyuncs.com\n"
  },
  {
    "path": "trackers.best.cn.list",
    "content": "https://1337.abcvg.info/announce\nhttps://api.ipv4online.uk/announce\nhttps://btn-prod.ghostchu-services.top/tracker/announce\nhttps://p2p.azu.red/announce\nhttps://sparkle.ghostchu-services.top/tracker/announce\nhttps://sparkle.ghostchu-services.top/announce\nhttps://tr.nyacat.pw/announce\nhttps://tracker.cloudit.top/announce\nhttps://tracker.gcrenwp.top/announce\nhttps://tracker.ipfsscan.io/announce\nhttps://tracker.kmzs123.cn/announce\nhttps://tracker.leechshield.link/announce\nhttps://tracker.lilithraws.org/announce\nhttps://tracker.pmman.tech/announce\nhttps://tracker.tamersunion.org/announce\nhttps://tracker1.520.jp/announce\nhttps://trackers.mlsub.net/announce\nhttps://tracker-zhuqiy.dgj055.icu/announce\nhttps://www.peckservers.com:9443/announce\nhttp://[2a04:ac00:1:3dd8::1:2710]:2710/announce\nhttp://0d.kebhana.mx/announce\nhttp://1337.abcvg.info/announce\nhttp://207.241.226.111:6969/announce\nhttp://207.241.231.226:6969/announce\nhttp://35.227.59.57:2701/announce\nhttp://bittorrent-tracker.e-n-c-r-y-p-t.net:1337/announce\nhttp://bt.poletracker.org:2710/announce\nhttp://bt.sc-ol.com:2710/announce\nhttp://bt1.xxxxbt.cc:6969/announce\nhttp://btfile.sdo.com:6961/announce\nhttp://btracker.top:11451/announce\nhttp://buny.uk:6969/announce\nhttp://bvarf.tracker.sh:2086/announce\nhttp://canardscitrons.nohost.me:6969/announce\nhttp://ch3oh.ru:6969/announce\nhttp://exodus.desync.com:6969/announce\nhttp://finbytes.org/announce.php\nhttp://fleira.no:6969/announce\nhttp://ftp.pet:6969/announce\nhttp://home.yxgz.club:6969/announce\nhttp://ipv4announce.sktorrent.eu:6969/announce\nhttp://ipv6.rer.lol:6969/announce\nhttp://open.acgtracker.com:1096/announce\nhttp://open.tracker.cl:1337/announce\nhttp://p4p.arenabg.com:1337/announce\nhttp://public.tracker.vraphim.com:6969/announce\nhttp://retracker.hotplug.ru:2710/announce\nhttp://retracker.spark-rostov.ru/announce\nhttp://saltwood.top:6969/announce\nhttp://seeders-paradise.org/announce\nhttp://servandroidkino.ru/announce\nhttp://share.dmhy.org/annonuce\nhttp://share.hkg-fansub.info/announce.php\nhttp://shubt.net:2710/announce\nhttp://smurfsoft.com:6969/announce\nhttp://sukebei.tracker.wf:8888/announce\nhttp://t.jaekr.sh:6969/announce\nhttp://t.nyaatracker.com/announce\nhttp://t.overflow.biz:6969/announce\nhttp://taciturn-shadow.spb.ru:6969/announce\nhttp://tr.kxmp.cf/announce\nhttp://tr.nyacat.pw/announce\nhttp://tr1.aag.moe:2095/announce\nhttp://tracker.bittor.pw:1337/announce\nhttp://tracker.bt-hash.com/announce\nhttp://tracker.bz/announce\nhttp://tracker.corpscorp.online/announce\nhttp://tracker.dler.com:6969/announce\nhttp://tracker.dler.org:6969/announce\nhttp://tracker.edkj.club:6969/announce\nhttp://tracker.ipv6tracker.org/announce\nhttp://tracker.lintk.me:2710/announce\nhttp://tracker.moxing.party:6969/announce\nhttp://tracker.openbittorrent.com/announce\nhttp://tracker.peckservers.com:9000/announce\nhttp://tracker.qu.ax:6969/announce\nhttp://tracker.renfei.net:8080/announce\nhttp://tracker.sbsub.com:2710/announce\nhttp://tracker.srv00.com:6969/announce\nhttp://tracker.torrentino.com/announce\nhttp://tracker.vanitycore.co:6969/announce\nhttp://tracker.vraphim.com:6969/announce\nhttp://tracker.waaa.moe:6969/announce\nhttp://tracker.xiaoduola.xyz:6969/announce\nhttp://tracker1.bt.moack.co.kr/announce\nhttp://tracker1.itzmx.com:8080/announce\nhttp://tracker1.torrentino.com/announce\nhttp://tracker2.dler.org/announce\nhttp://tracker2.torrentino.com/announce\nhttp://tracker3.itzmx.com:6961/announce\nhttp://tracker3.torrentino.com/announce\nhttp://tracker4.itzmx.com:2710/announce\nhttp://tracker4.torrentino.com/announce\nhttp://tracker810.xyz:11450/announce\nhttp://tracker-zhuqiy.dgj055.icu/announce\nhttp://wepzone.net:6969/announce\nhttp://www.genesis-sp.org:2710/announce\nhttp://www.torrentsnipe.info:2701/announce\nudp://[2a03:7220:8083:cd00::1]:451/announce\nudp://[2a04:ac00:1:3dd8::1:2710]:2710/announce\nudp://[2a0f:e586:f:f::81]:6969/announce\nudp://109.201.134.183:80/announce\nudp://185.243.218.213:80/announce\nudp://186.10.170.97:1337/announce\nudp://208.83.20.20:6969/announce\nudp://23.153.248.83:6969/announce\nudp://23.157.120.14:6969/announce\nudp://23.168.232.9:1337/announce\nudp://34.89.91.10:1337/announce\nudp://34.89.91.10:6969/announce\nudp://34.94.76.146:6969/announce\nudp://45.9.60.30:6969/announce\nudp://52.58.128.163:6969/announce\nudp://83.102.180.21:80/announce\nudp://91.216.110.53:451/announce\nudp://93.158.213.92:1337/announce\nudp://bt.sc-ol.com:2710/announce\nudp://coppersurfer.tk:6969/announce\nudp://open.demonii.com:1337/announce\nudp://retracker.hotplug.ru:2710/announce\nudp://tracker.openbittorrent.com:80/announce\n"
  },
  {
    "path": "trackers.list",
    "content": "https://1337.abcvg.info/announce\nhttps://1337.abcvg.info:443/announce\nhttps://abir0dev.github.io/announce\nhttps://api.ipv4online.uk:443/announce\nhttps://btn-prod.ghostchu-services.top/tracker/announce\nhttps://carbon-bonsai-621.appspot.com/announce\nhttps://chihaya-heroku.120181311.xyz/announce\nhttps://open.acgnxtracker.com/announce\nhttps://opentracker.acgnx.se/announce\nhttps://opentracker.cc/announce\nhttps://opentracker.i2p.rocks/announce\nhttps://opentracker.i2p.rocks:443/announce\nhttps://p2p.azu.red:443/announce\nhttps://sparkle.ghostchu-services.top/tracker/announce\nhttps://sparkle.ghostchu-services.top:443/announce\nhttps://t.btcland.xyz/announce\nhttps://t.zerg.pw:443/announce\nhttps://t-115.rhcloud.com/only_for_ylbud\nhttps://tr.abiir.top/announce\nhttps://tr.abir.ga/announce\nhttps://tr.abir.ga:443/announce\nhttps://tr.abirxo.cf/announce\nhttps://tr.bangumi.moe:9696/announce\nhttps://tr.burnabyhighstar.com/announce\nhttps://tr.burnabyhighstar.com:443/announce\nhttps://tr.doogh.club/announce\nhttps://tr.fuckbitcoin.xyz/announce\nhttps://tr.highstar.shop/announce\nhttps://tr.nyacat.pw/announce\nhttps://tr.nyacat.pw:443/announce\nhttps://tr.ready4.icu/announce\nhttps://tr.ready4.icu:443/announce\nhttps://tr.steins-gate.moe:2096/announce\nhttps://tr.torland.ga/announce\nhttps://tracker.babico.name.tr/announce\nhttps://tracker.bt4g.com:443/announce\nhttps://tracker.cloudit.top:443/announce\nhttps://tracker.coalition.space/announce\nhttps://tracker.cyber-hub.net/announce\nhttps://tracker.cyber-hub.net:443/announce\nhttps://tracker.feb217.tk:8443/announce\nhttps://tracker.foreverpirates.co/announce\nhttps://tracker.foreverpirates.co:443/announce\nhttps://tracker.gbitt.info/announce\nhttps://tracker.gcrenwp.top/announce\nhttps://tracker.gcrenwp.top:443/announce\nhttps://tracker.imgoingto.icu/announce\nhttps://tracker.imgoingto.icu:443/announce\nhttps://tracker.ipfsscan.io:443/announce\nhttps://tracker.iriseden.fr/announce\nhttps://tracker.itscraftsoftware.my.id:443/announce\nhttps://tracker.kmzs123.cn/announce\nhttps://tracker.kuroy.me/announce\nhttps://tracker.kuroy.me:443/announce\nhttps://tracker.leechshield.link:443/announce\nhttps://tracker.lelux.fi/announce\nhttps://tracker.lilithraws.cf/announce\nhttps://tracker.lilithraws.org/announce\nhttps://tracker.lilithraws.org:443/announce\nhttps://tracker.mlsub.net:443/announce\nhttps://tracker.nanoha.org/announce\nhttps://tracker.nanoha.org:443/announce\nhttps://tracker.nitrix.me/announce\nhttps://tracker.parrotsec.org/announce\nhttps://tracker.pmman.tech:443/announce\nhttps://tracker.sloppyta.co/announce\nhttps://tracker.tamersunion.org/announce\nhttps://tracker.tamersunion.org:443/announce\nhttps://tracker.vectahosting.eu:8443/announce\nhttps://tracker.yarr.pt/announce\nhttps://tracker.yemekyedim.com/announce\nhttps://tracker.yemekyedim.com:443/announce\nhttps://tracker1.520.jp/announce\nhttps://tracker1.520.jp:443/announce\nhttps://tracker6.lelux.fi/announce\nhttps://trackers.mlsub.net/announce\nhttps://trackers.mlsub.net:443/announce\nhttps://tracker-zhuqiy.dgj055.icu:443/announce\nhttps://trackme.theom.nz/announce\nhttps://www.peckservers.com:9443/announce\nhttp://[2001:1b10:1000:8101:0:242:ac11:2]:6969/announce\nhttp://[2001:470:1:189:0:1:2:3]:6969/announce\nhttp://[2a04:ac00:1:3dd8::1:2710]:2710/announce\nhttp://0123456789nonexistent.com:80/announce\nhttp://0d.kebhana.mx:443/announce\nhttp://104.143.10.186:8000/announce\nhttp://104.238.198.186:8000/announce\nhttp://1337.abcvg.info/announce\nhttp://1337.abcvg.info:80/announce\nhttp://207.241.226.111:6969/announce\nhttp://207.241.231.226:6969/announce\nhttp://5rt.tace.ru:60889/announce\nhttp://94.228.192.98/announce\nhttp://bigfoot1942.sektori.org:6969/announce\nhttp://bittorrent-tracker.e-n-c-r-y-p-t.net:1337/announce\nhttp://bt.3kb.xyz/announce\nhttp://bt.endpot.com:80/announce\nhttp://bt.okmp3.ru:2710/announce\nhttp://bt.poletracker.org:2710/announce\nhttp://bt.sc-ol.com:2710/announce\nhttp://bt1.xxxxbt.cc:6969/announce\nhttp://btfile.sdo.com:6961/announce\nhttp://btracker.top:11451/announce\nhttp://buny.uk:6969/announce\nhttp://bvarf.tracker.sh:2086/announce\nhttp://canardscitrons.nohost.me:6969/announce\nhttp://ch3oh.ru:6969/announce\nhttp://cloud.nyap2p.com:8080/announce\nhttp://dn42.smrsh.net:6969/announce\nhttp://exodus.desync.com:6969/announce\nhttp://finbytes.org:80/announce.php\nhttp://fleira.no:6969/announce\nhttp://fosstorrents.com:6969/announce\nhttp://ftp.pet:6969/announce\nhttp://fxtt.ru/announce\nhttp://highteahop.top:6960/announce\nhttp://home.yxgz.club:6969/announce\nhttp://home.yxgz.vip:6969/announce\nhttp://ipv4announce.sktorrent.eu:6969/announce\nhttp://ipv6.1337.cx:6969/announce\nhttp://ipv6.govt.hu:6969/announce\nhttp://ipv6.rer.lol:6969/announce\nhttp://mediaclub.tv/announce.php\nhttp://milanesitracker.tekcities.com/announce\nhttp://montreal.nyap2p.com:8080/announce\nhttp://nyaa.tracker.wf:7777/announce\nhttp://open.acgnxtracker.com/announce\nhttp://open.acgtracker.com:1096/announce\nhttp://open.miotracker.com/announce\nhttp://open.nyaatorrents.info:6544/announce\nhttp://open.tracker.cl:1337/announce\nhttp://open.tracker.ink:6969/announce\nhttp://open.trackerlist.xyz:80/announce\nhttp://openbittorrent.com:80/announce\nhttp://opentracker.acgnx.com:6869/announce\nhttp://opentracker.acgnx.se/announce\nhttp://opentracker.i2p.rocks:6969/announce\nhttp://opentracker.xyz/announce\nhttp://p2p.0g.cx:6969/announce\nhttp://p4p.arenabg.com:1337/announce\nhttp://parag.rs:6969/announce\nhttp://pow7.com/announce\nhttp://public.tracker.vraphim.com:6969/announce\nhttp://pubt.net:2710/announce\nhttp://retracker.hotplug.ru:2710/announce\nhttp://retracker.spark-rostov.ru:80/announce\nhttp://rfc5746.mywaifu.best:6969/announce\nhttp://rt.optizone.ru/announce\nhttp://rt.tace.ru/announce\nhttp://saltwood.top:6969/announce\nhttp://seeders-paradise.org:80/announce\nhttp://servandroidkino.ru:80/announce\nhttp://share.camoe.cn:8080/announce\nhttp://share.dmhy.org/annonuce\nhttp://share.hkg-fansub.info:80/announce.php\nhttp://shubt.net:2710/announce\nhttp://siambit.org/announce.php\nhttp://smurfsoft.com:6969/announce\nhttp://sukebei.tracker.wf:8888/announce\nhttp://t.acg.rip:6699/announce\nhttp://t.jaekr.sh:6969/announce\nhttp://t.nyaatracker.com/announce\nhttp://t.nyaatracker.com:80/announce\nhttp://t.overflow.biz:6969/announce\nhttp://t.publictracker.xyz:6969/announce\nhttp://t2.popgo.org:7456/annonce\nhttp://taciturn-shadow.spb.ru:6969/announce\nhttp://tk.greedland.net/announce\nhttp://tk.greedland.net:80/announce\nhttp://tk.nvacg.org:3333/announce\nhttp://torrentsmd.com:8080/announce\nhttp://torrenttracker.nwc.acsalaska.net:6969/announce\nhttp://tr.bangumi.moe:6969/announce\nhttp://tr.cili001.com:8070/announce\nhttp://tr.kxmp.cf/announce\nhttp://tr.kxmp.cf:80/announce\nhttp://tr.nyacat.pw/announce\nhttp://tr.nyacat.pw:80/announce\nhttp://tr1.aag.moe:2095/announce\nhttp://tracker.acgnx.se/announce\nhttp://tracker.birkenwald.de:6969/announce\nhttp://tracker.bittor.pw:1337/announce\nhttp://tracker.bt4g.com:2095/announce\nhttp://tracker.btcake.com/announce\nhttp://tracker.bt-hash.com:80/announce\nhttp://tracker.bz:80/announce\nhttp://tracker.corpscorp.online:80/announce\nhttp://tracker.dler.com:6969/announce\nhttp://tracker.dler.org:6969/announce\nhttp://tracker.dmcomic.org:2710/announce\nhttp://tracker.dutchtracking.nl/announce\nhttp://tracker.edkj.club:6969/announce\nhttp://tracker.electro-torrent.pl/announce\nhttp://tracker.electro-torrent.pl:80/announce\nhttp://tracker.files.fm:6969/announce\nhttp://tracker.gbitt.info/announce\nhttp://tracker.ipv6tracker.org/announce\nhttp://tracker.ipv6tracker.org:80/announce\nhttp://tracker.ipv6tracker.ru/announce\nhttp://tracker.k.vu:6969/announce\nhttp://tracker.kamigami.org:2710/announce\nhttp://tracker.ktxp.com:6868/announce\nhttp://tracker.ktxp.com:7070/announce\nhttp://tracker.lelux.fi/announce\nhttp://tracker.lintk.me:2710/announce\nhttp://tracker.loadbt.com:6969/announce\nhttp://tracker.merded.xyz:8000/announce\nhttp://tracker.moxing.party:6969/announce\nhttp://tracker.mywaifu.best:6969/announce\nhttp://tracker.noobsubs.net/announce\nhttp://tracker.nucozer-tracker.ml:2710/announce\nhttp://tracker.openbittorrent.com/announce\nhttp://tracker.openbittorrent.com:80/announce\nhttp://tracker.opentrackr.org:1337/announce\nhttp://tracker.peckservers.com:9000/announce\nhttp://tracker.qu.ax:6969/announce\nhttp://tracker.renfei.net:8080/announce\nhttp://tracker.sbsub.com:2710/announce\nhttp://tracker.skyts.net:6969/announce\nhttp://tracker.srv00.com:6969/announce\nhttp://tracker.swateam.org.uk:2710/announce\nhttp://tracker.tfile.co/announce\nhttp://tracker.tfile.me/announce\nhttp://tracker.torrentino.com/announce\nhttp://tracker.trackerfix.com/announce\nhttp://tracker.vanitycore.co:6969/announce\nhttp://tracker.vraphim.com:6969/announce\nhttp://tracker.waaa.moe:6969/announce\nhttp://tracker.xiaoduola.xyz:6969/announce\nhttp://tracker.zerobytes.xyz:1337/announce\nhttp://tracker1.bt.moack.co.kr/announce\nhttp://tracker1.bt.moack.co.kr:80/announce\nhttp://tracker1.itzmx.com:8080/announce\nhttp://tracker1.torrentino.com/announce\nhttp://tracker2.dler.org/announce\nhttp://tracker2.dler.org:80/announce\nhttp://tracker2.itzmx.com:6961/announce\nhttp://tracker2.torrentino.com/announce\nhttp://tracker3.itzmx.com:6961/announce\nhttp://tracker3.torrentino.com/announce\nhttp://tracker4.itzmx.com:2710/announce\nhttp://tracker4.torrentino.com/announce\nhttp://tracker5.torrentino.com/announce\nhttp://tracker810.xyz:11450/announce\nhttp://tracker-cdn.moeking.me:2095/announce\nhttp://tracker-zhuqiy.dgj055.icu:80/announce\nhttp://uraniumhexafluori.de:1919/announce\nhttp://v6-tracker.0g.cx:6969/announce\nhttp://vps02.net.orel.ru/announce\nhttp://wepzone.net:6969/announce\nhttp://www.all4nothin.net/announce.php\nhttp://www.all4nothin.net:80/announce.php\nhttp://www.genesis-sp.org:2710/announce\nhttp://www.torrentsnipe.info:2701/announce\nhttp://www.wareztorrent.com/announce\nhttp://www.wareztorrent.com:80/announce\nudp://[2001:1b10:1000:8101:0:242:ac11:2]:6969/announce\nudp://[2001:470:1:189:0:1:2:3]:6969/announce\nudp://[2a03:7220:8083:cd00::1]:451/announce\nudp://[2a04:ac00:1:3dd8::1:2710]:2710/announce\nudp://[2a0f:e586:f:f::220]:6969/announce\nudp://[2a0f:e586:f:f::81]:6969/announce\nudp://104.143.10.186:8000/announce\nudp://104.238.198.186:8000/announce\nudp://1c.premierzal.ru:6969/announce\nudp://207.241.226.111:6969/announce\nudp://207.241.231.226:6969/announce\nudp://212.1.226.176:2710/announce\nudp://47.ip-51-68-199.eu:6969/announce\nudp://52.58.128.163:6969/announce\nudp://6ahddutb1ucc3cp.ru:6969/announce\nudp://6rt.tace.ru:80/announce\nudp://78.30.254.12:2710/announce\nudp://7p41a8967.wicp.vip:22222/announce\nudp://9.rarbg.com:2810/announce\nudp://9.rarbg.com:2880/announce\nudp://9.rarbg.com:2930/announce\nudp://9.rarbg.com:2940/announce\nudp://9.rarbg.me:2710/announce\nudp://91.216.110.52:451/announce\nudp://aaa.army:8866/announce\nudp://aarsen.me:6969/announce\nudp://abufinzio.monocul.us:6969/announce\nudp://acxx.de:6969/announce\nudp://admin.52ywp.com:6969/announce\nudp://admin.videoenpoche.info:6969/announce\nudp://aegir.sexy:6969/announce\nudp://amigacity.xyz:6969/announce\nudp://api.clasificadosrapidos.com:53/announce\nudp://app.icon256.com:8000/announce\nudp://astrr.ru:6969/announce\nudp://bandito.byterunner.io:6969/announce\nudp://bclearning.top:6969/announce\nudp://bedro.cloud:6969/announce\nudp://bittorrent-tracker.e-n-c-r-y-p-t.net:1337/announce\nudp://black-bird.ynh.fr:6969/announce\nudp://blokas.io:6969/announce\nudp://bt.ktrackers.com:6666/announce\nudp://bt.oiyo.tk:6969/announce\nudp://bt.sc-ol.com:2710/announce\nudp://bt1.archive.org:6969/announce\nudp://bt2.archive.org:6969/announce\nudp://btt.service.gongt.me:43079/announce\nudp://bubu.mapfactor.com:6969/announce\nudp://camera.lei001.com:6969/announce\nudp://canardscitrons.nohost.me:6969/announce\nudp://carr.codes:6969/announce\nudp://cdn-1.gamecoast.org:6969/announce\nudp://code2chicken.nl:6969/announce\nudp://concen.org:6969/announce\nudp://coppersurfer.tk:6969/announce\nudp://cutiegirl.ru:6969/announce\nudp://d13bttrck.duckdns.org:6969/announce\nudp://d40969.acod.regrucolo.ru:6969/announce\nudp://daveking.com:6969/announce\nudp://discord.heihachi.pw:6969/announce\nudp://ec2-18-191-163-220.us-east-2.compute.amazonaws.com:6969/announce\nudp://edu.uifr.ru:6969/announce\nudp://engplus.ru:6969/announce\nudp://epider.me:6969/announce\nudp://evan.im:6969/announce\nudp://exodus.desync.com:6969/announce\nudp://explodie.org:6969/announce\nudp://fe.dealclub.de:6969/announce\nudp://htz3.noho.st:6969/announce\nudp://inferno.demonoid.is:3391/announce\nudp://ipv4.rer.lol:2710/announce\nudp://ipv4.tracker.harry.lu:80/announce\nudp://ipv6.69.mu:6969/announce\nudp://ipv6.babico.name.tr:8000/announce\nudp://ipv6.tracker.harry.lu:80/announce\nudp://ipv6.tracker.monitorit4.me:6969/announce\nudp://isk.richardsw.club:6969/announce\nudp://ismaarino.com:1234/announce\nudp://johnrosen1.com:6969/announce\nudp://jutone.com:6969/announce\nudp://laze.cc:6969/announce\nudp://leet-tracker.moe:1337/announce\nudp://leet-tracker.moe:23861/announce\nudp://leet-tracker.moe:38151/announce\nudp://line-net.ru:6969/announce\nudp://linfan.moe:6969/announce\nudp://ln.mtahost.co:6969/announce\nudp://mail.artixlinux.org:6969/announce\nudp://mail.realliferpg.de:6969/announce\nudp://martin-gebhardt.eu:25/announce\nudp://mirror.aptus.co.tz:6969/announce\nudp://moonburrow.club:6969/announce\nudp://movies.zsw.ca:6969/announce\nudp://mts.tvbit.co:6969/announce\nudp://nagios.tks.sumy.ua:80/announce\nudp://new-line.net:6969/announce\nudp://ns1.monolithindustries.com:6969/announce\nudp://ns-1.x-fins.com:6969/announce\nudp://odd-hd.fr:6969/announce\nudp://open.4ever.tk:6969/announce\nudp://open.demonii.com:1337/announce\nudp://open.dstud.io:6969/announce\nudp://open.free-tracker.ga:6969/announce\nudp://open.publictracker.xyz:6969/announce\nudp://open.stealth.si:80/announce\nudp://open.tracker.cl:1337/announce\nudp://open.tracker.ink:6969/announce\nudp://open.u-p.pw:6969/announce\nudp://opentor.org:2710/announce\nudp://opentracker.i2p.rocks:6969/announce\nudp://opentracker.io:6969/announce\nudp://p2p.publictracker.xyz:6969/announce\nudp://p4p.arenabg.ch:1337/announce\nudp://p4p.arenabg.com:1337/announce\nudp://peru.subventas.com:53/announce\nudp://pow7.com:80/announce\nudp://private.anonseed.com:6969/announce\nudp://psyco.fr:6969/announce\nudp://public.publictracker.xyz:6969/announce\nudp://public.tracker.vraphim.com:6969/announce\nudp://rep-art.ynh.fr:6969/announce\nudp://retracker.hotplug.ru:2710/announce\nudp://retracker.lanta.me:2710/announce\nudp://retracker.lanta-net.ru:2710/announce\nudp://retracker.netbynet.ru:2710/announce\nudp://retracker01-msk-virt.corbina.net:80/announce\nudp://run.publictracker.xyz:6969/announce\nudp://ryjer.com:6969/announce\nudp://sanincode.com:6969/announce\nudp://sd-161673.dedibox.fr:6969/announce\nudp://seedpeer.net:6969/announce\nudp://serpb.vpsburti.com:6969/announce\nudp://static.46.73.46.78.clients.your-server.de:8000/announce\nudp://static.54.161.216.95.clients.your-server.de:6969/announce\nudp://storage.groupees.com:6969/announce\nudp://t.nyaatracker.com:80/announce\nudp://t.overflow.biz:6969/announce\nudp://t.zerg.pw:6969/announce\nudp://t2.leech.ie:1337/announce\nudp://tamas3.ynh.fr:6969/announce\nudp://thagoat.rocks:6969/announce\nudp://thetracker.org:80/announce\nudp://thinking.duckdns.org:6969/announce\nudp://thouvenin.cloud:6969/announce\nudp://tk2v6.trackerservers.com:8080/announce\nudp://torrentclub.online:54123/announce\nudp://torrentclub.space:6969/announce\nudp://torrents.artixlinux.org:6969/announce\nudp://tr.bangumi.moe:6969/announce\nudp://tr.cili001.com:8070/announce\nudp://tr2.cubonegro.lol:6969/announce\nudp://tr4ck3r.duckdns.org:6969/announce\nudp://trackarr.org:6969/announce\nudp://tracker.0x.tf:6969/announce\nudp://tracker.0x7c0.com:6969/announce\nudp://tracker.4.babico.name.tr:3131/announce\nudp://tracker.altrosky.nl:6969/announce\nudp://tracker.auctor.tv:6969/announce\nudp://tracker.babico.name.tr:8000/announce\nudp://tracker.beeimg.com:6969/announce\nudp://tracker.birkenwald.de:6969/announce\nudp://tracker.bitsearch.to:1337/announce\nudp://tracker.bittor.pw:1337/announce\nudp://tracker.breizh.pm:6969/announce\nudp://tracker.ccp.ovh:6969/announce\nudp://tracker.cloaka.xyz:1337/announce\nudp://tracker.coppersurfer.tk:6969/announce\nudp://tracker.cubonegro.lol:6969/announce\nudp://tracker.cyberia.is:6969/announce\nudp://tracker.darkness.services:6969/announce\nudp://tracker.ddunlimited.net:6969/announce\nudp://tracker.deadorbit.nl:6969/announce\nudp://tracker.dix.tf:6969/announce\nudp://tracker.dler.com:6969/announce\nudp://tracker.dler.org:6969/announce\nudp://tracker.doko.moe:6969/announce\nudp://tracker.ds.is:6969/announce\nudp://tracker.dump.cl:6969/announce\nudp://tracker.edkj.club:6969/announce\nudp://tracker.e-utp.net:6969/announce\nudp://tracker.ex.ua:80/announce\nudp://tracker.farted.net:6969/announce\nudp://tracker.fatkhoala.org:13710/announce\nudp://tracker.filemail.com:6969/announce\nudp://tracker.fnix.net:6969/announce\nudp://tracker.gigantino.net:6969/announce\nudp://tracker.gmi.gd:6969/announce\nudp://tracker.internetwarriors.net:1337/announce\nudp://tracker.jamesthebard.net:6969/announce\nudp://tracker.jonaslsa.com:6969/announce\nudp://tracker.jordan.im:6969/announce\nudp://tracker.joybomb.tw:6969/announce\nudp://tracker.kamigami.org:2710/announce\nudp://tracker.leech.ie:1337/announce\nudp://tracker.lelux.fi:6969/announce\nudp://tracker.loadbt.com:6969/announce\nudp://tracker.merded.xyz:8000/announce\nudp://tracker.moeking.eu.org:6969/announce\nudp://tracker.moeking.me:6969/announce\nudp://tracker.monitorit4.me:6969/announce\nudp://tracker.ololosh.space:6969/announce\nudp://tracker.openbittorrent.com:6969/announce\nudp://tracker.openbittorrent.com:80/announce\nudp://tracker.opentrackr.org:1337/announce\nudp://tracker.pomf.se:80/announce\nudp://tracker.prq.to:80/announce\nudp://tracker.publicbt.com:80/announce\nudp://tracker.publictracker.xyz:6969/announce\nudp://tracker.qu.ax:6969/announce\nudp://tracker.shkinev.me:6969/announce\nudp://tracker.sigterm.xyz:6969/announce\nudp://tracker.skynetcloud.site:6969/announce\nudp://tracker.skyts.net:6969/announce\nudp://tracker.srv00.com:6969/announce\nudp://tracker.swateam.org.uk:2710/announce\nudp://tracker.sylphix.com:6969/announce\nudp://tracker.theoks.net:6969/announce\nudp://tracker.tiny-vps.com:6969/announce\nudp://tracker.torrent.eu.org:451/announce\nudp://tracker.torrust-demo.com:6969/announce\nudp://tracker.tryhackx.org:6969/announce\nudp://tracker.uw0.xyz:6969/announce\nudp://tracker.v6speed.org:6969/announce\nudp://tracker.waaa.moe:6969/announce\nudp://tracker.zemoj.com:6969/announce\nudp://tracker.zerobytes.xyz:1337/announce\nudp://tracker0.ufibox.com:6969/announce\nudp://tracker1.bt.moack.co.kr:80/announce\nudp://tracker1.itzmx.com:8080/announce\nudp://tracker1.myporn.club:9337/announce\nudp://tracker2.dler.com:80/announce\nudp://tracker2.dler.org:80/announce\nudp://tracker2.itzmx.com:6961/announce\nudp://tracker3.itzmx.com:6961/announce\nudp://tracker4.itzmx.com:2710/announce\nudp://tracker4.leechshield.link:6969/announce\nudp://tracker6.lelux.fi:6969/announce\nudp://trackerb.jonaslsa.com:6969/announce\nudp://tracker-udp.gbitt.info:80/announce\nudp://tsundere.pw:6969/announce\nudp://ttk2.nbaonlineservice.com:6969/announce\nudp://u4.trakx.crim.ist:1337/announce\nudp://u6.trakx.crim.ist:1337/announce\nudp://udp.yarr.pt:1337/announce\nudp://udp-tracker.shittyurl.org:6969/announce\nudp://us-tracker.publictracker.xyz:6969/announce\nudp://v1046920.hosted-by-vdsina.ru:6969/announce\nudp://v2.iperson.xyz:6969/announce\nudp://valakas.rollo.dnsabr.com:2710/announce\nudp://vibe.community:6969/announce\nudp://vibe.sleepyinternetfun.xyz:1738/announce\nudp://wepzone.net:6969/announce\nudp://www.torrent.eu.org:451/announce\nudp://z.mercax.com:53/announce\nudp://zecircle.xyz:6969/announce\nws://hub.bugout.link:80/announce\nwss://tracker.openwebtorrent.com:443/announce\n"
  },
  {
    "path": "use_openssl_md5_sha1.patch",
    "content": "From: CarterLi <carter.li@eoitek.com>\nDate: Fri, 15 Jun 2018 14:58:09 +0800\nSubject: [PATCH] Use openssl md5 / sha1\nLink: https://github.com/kn007/patch/issues/5\n\ndiff -uNr a/auto/sources b/auto/sources\n--- a/auto/sources\t2020-03-03 23:04:21.000000000 +0800\n+++ b/auto/sources\t2020-03-13 22:00:37.317527023 +0800\n@@ -60,8 +60,6 @@\n            src/core/ngx_file.c \\\n            src/core/ngx_crc32.c \\\n            src/core/ngx_murmurhash.c \\\n-           src/core/ngx_md5.c \\\n-           src/core/ngx_sha1.c \\\n            src/core/ngx_rbtree.c \\\n            src/core/ngx_radix_tree.c \\\n            src/core/ngx_slab.c \\\ndiff -uNr a/src/core/ngx_md5.c b/src/core/ngx_md5.c\n--- a/src/core/ngx_md5.c\t2020-03-03 23:04:21.000000000 +0800\n+++ /dev/null\t1970-01-01 08:00:00.000000000 +0800\n@@ -1,283 +0,0 @@\n-\n-/*\n- * An internal implementation, based on Alexander Peslyak's\n- * public domain implementation:\n- * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5\n- */\n-\n-\n-#include <ngx_config.h>\n-#include <ngx_core.h>\n-#include <ngx_md5.h>\n-\n-\n-static const u_char *ngx_md5_body(ngx_md5_t *ctx, const u_char *data,\n-    size_t size);\n-\n-\n-void\n-ngx_md5_init(ngx_md5_t *ctx)\n-{\n-    ctx->a = 0x67452301;\n-    ctx->b = 0xefcdab89;\n-    ctx->c = 0x98badcfe;\n-    ctx->d = 0x10325476;\n-\n-    ctx->bytes = 0;\n-}\n-\n-\n-void\n-ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size)\n-{\n-    size_t  used, free;\n-\n-    used = (size_t) (ctx->bytes & 0x3f);\n-    ctx->bytes += size;\n-\n-    if (used) {\n-        free = 64 - used;\n-\n-        if (size < free) {\n-            ngx_memcpy(&ctx->buffer[used], data, size);\n-            return;\n-        }\n-\n-        ngx_memcpy(&ctx->buffer[used], data, free);\n-        data = (u_char *) data + free;\n-        size -= free;\n-        (void) ngx_md5_body(ctx, ctx->buffer, 64);\n-    }\n-\n-    if (size >= 64) {\n-        data = ngx_md5_body(ctx, data, size & ~(size_t) 0x3f);\n-        size &= 0x3f;\n-    }\n-\n-    ngx_memcpy(ctx->buffer, data, size);\n-}\n-\n-\n-void\n-ngx_md5_final(u_char result[16], ngx_md5_t *ctx)\n-{\n-    size_t  used, free;\n-\n-    used = (size_t) (ctx->bytes & 0x3f);\n-\n-    ctx->buffer[used++] = 0x80;\n-\n-    free = 64 - used;\n-\n-    if (free < 8) {\n-        ngx_memzero(&ctx->buffer[used], free);\n-        (void) ngx_md5_body(ctx, ctx->buffer, 64);\n-        used = 0;\n-        free = 64;\n-    }\n-\n-    ngx_memzero(&ctx->buffer[used], free - 8);\n-\n-    ctx->bytes <<= 3;\n-    ctx->buffer[56] = (u_char) ctx->bytes;\n-    ctx->buffer[57] = (u_char) (ctx->bytes >> 8);\n-    ctx->buffer[58] = (u_char) (ctx->bytes >> 16);\n-    ctx->buffer[59] = (u_char) (ctx->bytes >> 24);\n-    ctx->buffer[60] = (u_char) (ctx->bytes >> 32);\n-    ctx->buffer[61] = (u_char) (ctx->bytes >> 40);\n-    ctx->buffer[62] = (u_char) (ctx->bytes >> 48);\n-    ctx->buffer[63] = (u_char) (ctx->bytes >> 56);\n-\n-    (void) ngx_md5_body(ctx, ctx->buffer, 64);\n-\n-    result[0] = (u_char) ctx->a;\n-    result[1] = (u_char) (ctx->a >> 8);\n-    result[2] = (u_char) (ctx->a >> 16);\n-    result[3] = (u_char) (ctx->a >> 24);\n-    result[4] = (u_char) ctx->b;\n-    result[5] = (u_char) (ctx->b >> 8);\n-    result[6] = (u_char) (ctx->b >> 16);\n-    result[7] = (u_char) (ctx->b >> 24);\n-    result[8] = (u_char) ctx->c;\n-    result[9] = (u_char) (ctx->c >> 8);\n-    result[10] = (u_char) (ctx->c >> 16);\n-    result[11] = (u_char) (ctx->c >> 24);\n-    result[12] = (u_char) ctx->d;\n-    result[13] = (u_char) (ctx->d >> 8);\n-    result[14] = (u_char) (ctx->d >> 16);\n-    result[15] = (u_char) (ctx->d >> 24);\n-\n-    ngx_memzero(ctx, sizeof(*ctx));\n-}\n-\n-\n-/*\n- * The basic MD5 functions.\n- *\n- * F and G are optimized compared to their RFC 1321 definitions for\n- * architectures that lack an AND-NOT instruction, just like in\n- * Colin Plumb's implementation.\n- */\n-\n-#define F(x, y, z)  ((z) ^ ((x) & ((y) ^ (z))))\n-#define G(x, y, z)  ((y) ^ ((z) & ((x) ^ (y))))\n-#define H(x, y, z)  ((x) ^ (y) ^ (z))\n-#define I(x, y, z)  ((y) ^ ((x) | ~(z)))\n-\n-/*\n- * The MD5 transformation for all four rounds.\n- */\n-\n-#define STEP(f, a, b, c, d, x, t, s)                                          \\\n-    (a) += f((b), (c), (d)) + (x) + (t);                                      \\\n-    (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s))));                \\\n-    (a) += (b)\n-\n-/*\n- * SET() reads 4 input bytes in little-endian byte order and stores them\n- * in a properly aligned word in host byte order.\n- *\n- * The check for little-endian architectures that tolerate unaligned\n- * memory accesses is just an optimization.  Nothing will break if it\n- * does not work.\n- */\n-\n-#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)\n-\n-#define SET(n)      (*(uint32_t *) &p[n * 4])\n-#define GET(n)      (*(uint32_t *) &p[n * 4])\n-\n-#else\n-\n-#define SET(n)                                                                \\\n-    (block[n] =                                                               \\\n-    (uint32_t) p[n * 4] |                                                     \\\n-    ((uint32_t) p[n * 4 + 1] << 8) |                                          \\\n-    ((uint32_t) p[n * 4 + 2] << 16) |                                         \\\n-    ((uint32_t) p[n * 4 + 3] << 24))\n-\n-#define GET(n)      block[n]\n-\n-#endif\n-\n-\n-/*\n- * This processes one or more 64-byte data blocks, but does not update\n- * the bit counters.  There are no alignment requirements.\n- */\n-\n-static const u_char *\n-ngx_md5_body(ngx_md5_t *ctx, const u_char *data, size_t size)\n-{\n-    uint32_t       a, b, c, d;\n-    uint32_t       saved_a, saved_b, saved_c, saved_d;\n-    const u_char  *p;\n-#if !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)\n-    uint32_t       block[16];\n-#endif\n-\n-    p = data;\n-\n-    a = ctx->a;\n-    b = ctx->b;\n-    c = ctx->c;\n-    d = ctx->d;\n-\n-    do {\n-        saved_a = a;\n-        saved_b = b;\n-        saved_c = c;\n-        saved_d = d;\n-\n-        /* Round 1 */\n-\n-        STEP(F, a, b, c, d, SET(0),  0xd76aa478, 7);\n-        STEP(F, d, a, b, c, SET(1),  0xe8c7b756, 12);\n-        STEP(F, c, d, a, b, SET(2),  0x242070db, 17);\n-        STEP(F, b, c, d, a, SET(3),  0xc1bdceee, 22);\n-        STEP(F, a, b, c, d, SET(4),  0xf57c0faf, 7);\n-        STEP(F, d, a, b, c, SET(5),  0x4787c62a, 12);\n-        STEP(F, c, d, a, b, SET(6),  0xa8304613, 17);\n-        STEP(F, b, c, d, a, SET(7),  0xfd469501, 22);\n-        STEP(F, a, b, c, d, SET(8),  0x698098d8, 7);\n-        STEP(F, d, a, b, c, SET(9),  0x8b44f7af, 12);\n-        STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17);\n-        STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22);\n-        STEP(F, a, b, c, d, SET(12), 0x6b901122, 7);\n-        STEP(F, d, a, b, c, SET(13), 0xfd987193, 12);\n-        STEP(F, c, d, a, b, SET(14), 0xa679438e, 17);\n-        STEP(F, b, c, d, a, SET(15), 0x49b40821, 22);\n-\n-        /* Round 2 */\n-\n-        STEP(G, a, b, c, d, GET(1),  0xf61e2562, 5);\n-        STEP(G, d, a, b, c, GET(6),  0xc040b340, 9);\n-        STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14);\n-        STEP(G, b, c, d, a, GET(0),  0xe9b6c7aa, 20);\n-        STEP(G, a, b, c, d, GET(5),  0xd62f105d, 5);\n-        STEP(G, d, a, b, c, GET(10), 0x02441453, 9);\n-        STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14);\n-        STEP(G, b, c, d, a, GET(4),  0xe7d3fbc8, 20);\n-        STEP(G, a, b, c, d, GET(9),  0x21e1cde6, 5);\n-        STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9);\n-        STEP(G, c, d, a, b, GET(3),  0xf4d50d87, 14);\n-        STEP(G, b, c, d, a, GET(8),  0x455a14ed, 20);\n-        STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5);\n-        STEP(G, d, a, b, c, GET(2),  0xfcefa3f8, 9);\n-        STEP(G, c, d, a, b, GET(7),  0x676f02d9, 14);\n-        STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20);\n-\n-        /* Round 3 */\n-\n-        STEP(H, a, b, c, d, GET(5),  0xfffa3942, 4);\n-        STEP(H, d, a, b, c, GET(8),  0x8771f681, 11);\n-        STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16);\n-        STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23);\n-        STEP(H, a, b, c, d, GET(1),  0xa4beea44, 4);\n-        STEP(H, d, a, b, c, GET(4),  0x4bdecfa9, 11);\n-        STEP(H, c, d, a, b, GET(7),  0xf6bb4b60, 16);\n-        STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23);\n-        STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4);\n-        STEP(H, d, a, b, c, GET(0),  0xeaa127fa, 11);\n-        STEP(H, c, d, a, b, GET(3),  0xd4ef3085, 16);\n-        STEP(H, b, c, d, a, GET(6),  0x04881d05, 23);\n-        STEP(H, a, b, c, d, GET(9),  0xd9d4d039, 4);\n-        STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11);\n-        STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16);\n-        STEP(H, b, c, d, a, GET(2),  0xc4ac5665, 23);\n-\n-        /* Round 4 */\n-\n-        STEP(I, a, b, c, d, GET(0),  0xf4292244, 6);\n-        STEP(I, d, a, b, c, GET(7),  0x432aff97, 10);\n-        STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15);\n-        STEP(I, b, c, d, a, GET(5),  0xfc93a039, 21);\n-        STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6);\n-        STEP(I, d, a, b, c, GET(3),  0x8f0ccc92, 10);\n-        STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15);\n-        STEP(I, b, c, d, a, GET(1),  0x85845dd1, 21);\n-        STEP(I, a, b, c, d, GET(8),  0x6fa87e4f, 6);\n-        STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10);\n-        STEP(I, c, d, a, b, GET(6),  0xa3014314, 15);\n-        STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21);\n-        STEP(I, a, b, c, d, GET(4),  0xf7537e82, 6);\n-        STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10);\n-        STEP(I, c, d, a, b, GET(2),  0x2ad7d2bb, 15);\n-        STEP(I, b, c, d, a, GET(9),  0xeb86d391, 21);\n-\n-        a += saved_a;\n-        b += saved_b;\n-        c += saved_c;\n-        d += saved_d;\n-\n-        p += 64;\n-\n-    } while (size -= 64);\n-\n-    ctx->a = a;\n-    ctx->b = b;\n-    ctx->c = c;\n-    ctx->d = d;\n-\n-    return p;\n-}\ndiff -uNr a/src/core/ngx_md5.h b/src/core/ngx_md5.h\n--- a/src/core/ngx_md5.h\t2020-03-03 23:04:21.000000000 +0800\n+++ b/src/core/ngx_md5.h\t2020-03-13 22:00:37.318527030 +0800\n@@ -12,17 +12,13 @@\n #include <ngx_config.h>\n #include <ngx_core.h>\n \n+#include <openssl/md5.h>\n \n-typedef struct {\n-    uint64_t  bytes;\n-    uint32_t  a, b, c, d;\n-    u_char    buffer[64];\n-} ngx_md5_t;\n+typedef MD5_CTX  ngx_md5_t;\n \n-\n-void ngx_md5_init(ngx_md5_t *ctx);\n-void ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size);\n-void ngx_md5_final(u_char result[16], ngx_md5_t *ctx);\n+#define ngx_md5_init    MD5_Init\n+#define ngx_md5_update  MD5_Update\n+#define ngx_md5_final   MD5_Final\n \n \n #endif /* _NGX_MD5_H_INCLUDED_ */\ndiff -uNr a/src/core/ngx_sha1.c b/src/core/ngx_sha1.c\n--- a/src/core/ngx_sha1.c\t2020-03-03 23:04:21.000000000 +0800\n+++ /dev/null\t1970-01-01 08:00:00.000000000 +0800\n@@ -1,294 +0,0 @@\n-\n-/*\n- * Copyright (C) Maxim Dounin\n- * Copyright (C) Nginx, Inc.\n- *\n- * An internal SHA1 implementation.\n- */\n-\n-\n-#include <ngx_config.h>\n-#include <ngx_core.h>\n-#include <ngx_sha1.h>\n-\n-\n-static const u_char *ngx_sha1_body(ngx_sha1_t *ctx, const u_char *data,\n-    size_t size);\n-\n-\n-void\n-ngx_sha1_init(ngx_sha1_t *ctx)\n-{\n-    ctx->a = 0x67452301;\n-    ctx->b = 0xefcdab89;\n-    ctx->c = 0x98badcfe;\n-    ctx->d = 0x10325476;\n-    ctx->e = 0xc3d2e1f0;\n-\n-    ctx->bytes = 0;\n-}\n-\n-\n-void\n-ngx_sha1_update(ngx_sha1_t *ctx, const void *data, size_t size)\n-{\n-    size_t  used, free;\n-\n-    used = (size_t) (ctx->bytes & 0x3f);\n-    ctx->bytes += size;\n-\n-    if (used) {\n-        free = 64 - used;\n-\n-        if (size < free) {\n-            ngx_memcpy(&ctx->buffer[used], data, size);\n-            return;\n-        }\n-\n-        ngx_memcpy(&ctx->buffer[used], data, free);\n-        data = (u_char *) data + free;\n-        size -= free;\n-        (void) ngx_sha1_body(ctx, ctx->buffer, 64);\n-    }\n-\n-    if (size >= 64) {\n-        data = ngx_sha1_body(ctx, data, size & ~(size_t) 0x3f);\n-        size &= 0x3f;\n-    }\n-\n-    ngx_memcpy(ctx->buffer, data, size);\n-}\n-\n-\n-void\n-ngx_sha1_final(u_char result[20], ngx_sha1_t *ctx)\n-{\n-    size_t  used, free;\n-\n-    used = (size_t) (ctx->bytes & 0x3f);\n-\n-    ctx->buffer[used++] = 0x80;\n-\n-    free = 64 - used;\n-\n-    if (free < 8) {\n-        ngx_memzero(&ctx->buffer[used], free);\n-        (void) ngx_sha1_body(ctx, ctx->buffer, 64);\n-        used = 0;\n-        free = 64;\n-    }\n-\n-    ngx_memzero(&ctx->buffer[used], free - 8);\n-\n-    ctx->bytes <<= 3;\n-    ctx->buffer[56] = (u_char) (ctx->bytes >> 56);\n-    ctx->buffer[57] = (u_char) (ctx->bytes >> 48);\n-    ctx->buffer[58] = (u_char) (ctx->bytes >> 40);\n-    ctx->buffer[59] = (u_char) (ctx->bytes >> 32);\n-    ctx->buffer[60] = (u_char) (ctx->bytes >> 24);\n-    ctx->buffer[61] = (u_char) (ctx->bytes >> 16);\n-    ctx->buffer[62] = (u_char) (ctx->bytes >> 8);\n-    ctx->buffer[63] = (u_char) ctx->bytes;\n-\n-    (void) ngx_sha1_body(ctx, ctx->buffer, 64);\n-\n-    result[0] = (u_char) (ctx->a >> 24);\n-    result[1] = (u_char) (ctx->a >> 16);\n-    result[2] = (u_char) (ctx->a >> 8);\n-    result[3] = (u_char) ctx->a;\n-    result[4] = (u_char) (ctx->b >> 24);\n-    result[5] = (u_char) (ctx->b >> 16);\n-    result[6] = (u_char) (ctx->b >> 8);\n-    result[7] = (u_char) ctx->b;\n-    result[8] = (u_char) (ctx->c >> 24);\n-    result[9] = (u_char) (ctx->c >> 16);\n-    result[10] = (u_char) (ctx->c >> 8);\n-    result[11] = (u_char) ctx->c;\n-    result[12] = (u_char) (ctx->d >> 24);\n-    result[13] = (u_char) (ctx->d >> 16);\n-    result[14] = (u_char) (ctx->d >> 8);\n-    result[15] = (u_char) ctx->d;\n-    result[16] = (u_char) (ctx->e >> 24);\n-    result[17] = (u_char) (ctx->e >> 16);\n-    result[18] = (u_char) (ctx->e >> 8);\n-    result[19] = (u_char) ctx->e;\n-\n-    ngx_memzero(ctx, sizeof(*ctx));\n-}\n-\n-\n-/*\n- * Helper functions.\n- */\n-\n-#define ROTATE(bits, word)  (((word) << (bits)) | ((word) >> (32 - (bits))))\n-\n-#define F1(b, c, d)  (((b) & (c)) | ((~(b)) & (d)))\n-#define F2(b, c, d)  ((b) ^ (c) ^ (d))\n-#define F3(b, c, d)  (((b) & (c)) | ((b) & (d)) | ((c) & (d)))\n-\n-#define STEP(f, a, b, c, d, e, w, t)                                          \\\n-    temp = ROTATE(5, (a)) + f((b), (c), (d)) + (e) + (w) + (t);               \\\n-    (e) = (d);                                                                \\\n-    (d) = (c);                                                                \\\n-    (c) = ROTATE(30, (b));                                                    \\\n-    (b) = (a);                                                                \\\n-    (a) = temp;\n-\n-\n-/*\n- * GET() reads 4 input bytes in big-endian byte order and returns\n- * them as uint32_t.\n- */\n-\n-#define GET(n)                                                                \\\n-    ((uint32_t) p[n * 4 + 3] |                                                \\\n-    ((uint32_t) p[n * 4 + 2] << 8) |                                          \\\n-    ((uint32_t) p[n * 4 + 1] << 16) |                                         \\\n-    ((uint32_t) p[n * 4] << 24))\n-\n-\n-/*\n- * This processes one or more 64-byte data blocks, but does not update\n- * the bit counters.  There are no alignment requirements.\n- */\n-\n-static const u_char *\n-ngx_sha1_body(ngx_sha1_t *ctx, const u_char *data, size_t size)\n-{\n-    uint32_t       a, b, c, d, e, temp;\n-    uint32_t       saved_a, saved_b, saved_c, saved_d, saved_e;\n-    uint32_t       words[80];\n-    ngx_uint_t     i;\n-    const u_char  *p;\n-\n-    p = data;\n-\n-    a = ctx->a;\n-    b = ctx->b;\n-    c = ctx->c;\n-    d = ctx->d;\n-    e = ctx->e;\n-\n-    do {\n-        saved_a = a;\n-        saved_b = b;\n-        saved_c = c;\n-        saved_d = d;\n-        saved_e = e;\n-\n-        /* Load data block into the words array */\n-\n-        for (i = 0; i < 16; i++) {\n-            words[i] = GET(i);\n-        }\n-\n-        for (i = 16; i < 80; i++) {\n-            words[i] = ROTATE(1, words[i - 3] ^ words[i - 8] ^ words[i - 14]\n-                                 ^ words[i - 16]);\n-        }\n-\n-        /* Transformations */\n-\n-        STEP(F1, a, b, c, d, e, words[0],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[1],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[2],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[3],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[4],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[5],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[6],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[7],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[8],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[9],  0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[10], 0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[11], 0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[12], 0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[13], 0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[14], 0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[15], 0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[16], 0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[17], 0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[18], 0x5a827999);\n-        STEP(F1, a, b, c, d, e, words[19], 0x5a827999);\n-\n-        STEP(F2, a, b, c, d, e, words[20], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[21], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[22], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[23], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[24], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[25], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[26], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[27], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[28], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[29], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[30], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[31], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[32], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[33], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[34], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[35], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[36], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[37], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[38], 0x6ed9eba1);\n-        STEP(F2, a, b, c, d, e, words[39], 0x6ed9eba1);\n-\n-        STEP(F3, a, b, c, d, e, words[40], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[41], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[42], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[43], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[44], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[45], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[46], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[47], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[48], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[49], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[50], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[51], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[52], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[53], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[54], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[55], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[56], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[57], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[58], 0x8f1bbcdc);\n-        STEP(F3, a, b, c, d, e, words[59], 0x8f1bbcdc);\n-\n-        STEP(F2, a, b, c, d, e, words[60], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[61], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[62], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[63], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[64], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[65], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[66], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[67], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[68], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[69], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[70], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[71], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[72], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[73], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[74], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[75], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[76], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[77], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[78], 0xca62c1d6);\n-        STEP(F2, a, b, c, d, e, words[79], 0xca62c1d6);\n-\n-        a += saved_a;\n-        b += saved_b;\n-        c += saved_c;\n-        d += saved_d;\n-        e += saved_e;\n-\n-        p += 64;\n-\n-    } while (size -= 64);\n-\n-    ctx->a = a;\n-    ctx->b = b;\n-    ctx->c = c;\n-    ctx->d = d;\n-    ctx->e = e;\n-\n-    return p;\n-}\ndiff -uNr a/src/core/ngx_sha1.h b/src/core/ngx_sha1.h\n--- a/src/core/ngx_sha1.h\t2020-03-03 23:04:21.000000000 +0800\n+++ b/src/core/ngx_sha1.h\t2020-03-13 22:00:37.319527037 +0800\n@@ -12,17 +12,13 @@\n #include <ngx_config.h>\n #include <ngx_core.h>\n \n+#include <openssl/sha.h>\n \n-typedef struct {\n-    uint64_t  bytes;\n-    uint32_t  a, b, c, d, e, f;\n-    u_char    buffer[64];\n-} ngx_sha1_t;\n+typedef SHA_CTX  ngx_sha1_t;\n \n-\n-void ngx_sha1_init(ngx_sha1_t *ctx);\n-void ngx_sha1_update(ngx_sha1_t *ctx, const void *data, size_t size);\n-void ngx_sha1_final(u_char result[20], ngx_sha1_t *ctx);\n+#define ngx_sha1_init    SHA1_Init\n+#define ngx_sha1_update  SHA1_Update\n+#define ngx_sha1_final   SHA1_Final\n \n \n #endif /* _NGX_SHA1_H_INCLUDED_ */\n"
  }
]