text:
var express = require('express');
var router = express.Router();
var request = require("request");
var stream = require('../app/models/stream');
var exam = require('../app/models/exam');
var test = require('../app/models/test');
var blogpost = require('../app/models/blogpost');
var coaching = require('../app/models/coaching');
var city = require('../app/models/city');
var fs = require('fs');
var path = require('path');
var moment = require('moment');

function slugify(string) {
  return string
    .toString()
    .trim()
    .toLowerCase()
    .replace(/\s+/g, "-")
    .replace(/[^\w\-]+/g, "")
    .replace(/\-\-+/g, "-")
    .replace(/^-+/, "")
    .replace(/-+$/, "");
};
router.post('/p0', function(req, res) {
    var urls = ["https://www.exambazaar.com"];
    res.json(urls);
});
router.post('/p1', function(req, res) {
    var prefix = "https://www.exambazaar.com/stream";
    var separator = "/";
    var urls = [];
    urls.push(prefix);
    var allStreams = stream.find({active: {$ne: false}}, {name:1}, function(err, allStreams) {
    if (!err){
        var nStreams = allStreams.length;
        var counter = 0;
        allStreams.forEach(function(thisStream, index){
            var thisURL = prefix + separator + encodeURIComponent(thisStream.name);
            urls.push(thisURL);
            counter+= 1;
            if(counter == nStreams){
                res.json(urls);
            }
        });
    } else {throw err;}
    });
});

router.post('/blogurls', function(req, res) {
    console.log('Starting Blog Urls');
    var urlStart = "<url>";
    var urlEnd = "</url>";
    var locStart = "<loc>";
    var locEnd = "</loc>";
    var lastmodStart = "<lastmod>";
    var lastmodEnd = "</lastmod>";
    var changefreqStart = "<changefreq>";
    var changefreqEnd = "</changefreq>";
    var priorityStart = "<priority>";
    var priorityEnd = "</priority>";
    var urlsetStart = "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">";
    var urlsetEnd = "</urlset>";
    var xmlStart = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";

    var prefix = "https://www.exambazaar.com/blogpost";
    var separator = "/";
    var urls = [];
    var blogurls = [];
    var blogurlsCurated = [];
    var allBlogs = blogpost.find({active: true}, {urlslug:1, _published: 1}, function(err, allBlogs) {
    if (!err){
        var nBlogs = allBlogs.length;
        var counter = 0;
        allBlogs.forEach(function(thisBlog, index){
            var thisURL = prefix + separator + thisBlog.urlslug;
            thisBlog.urlslug = thisURL;
            blogurls.push(thisBlog);
            counter+= 1;
            if(counter == nBlogs){
                res.json(blogurls);
            }
        });
    } else {throw err;}
    });
});

function generateXML(urlobjs){
    var urlStart = "<url>";
    var urlEnd = "</url>";
    var locStart = "<loc>";
    var locEnd = "</loc>";
    var lastmodStart = "<lastmod>";
    var lastmodEnd = "</lastmod>";
    var changefreqStart = "<changefreq>";
    var changefreqEnd = "</changefreq>";
    var priorityStart = "<priority>";
    var priorityEnd = "</priority>";
    var urlsetStart = "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">";
    var urlsetEnd = "</urlset>";
    var xmlStart = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";

    var xmlcontent = "";
    if(urlobjs && urlobjs.length > 0){
        var nobjs = urlobjs.length;

        xmlcontent += xmlStart;
        xmlcontent += urlsetStart;

        urlobjs.forEach(function(thisURL, index){
            if(thisURL.urlslug && thisURL.urlslug.length > 0){
                if(!thisURL.changefreq){
                    thisURL.changefreq = "weekly";
                }
                if(!thisURL.priority){
                    thisURL.priority = 0.5;
                }
                var thisURLText = "";
                thisURLText += urlStart;
                thisURLText += locStart;
                thisURLText += thisURL.urlslug;
                thisURLText += locEnd;
                if(thisURL.changefreq){
                   thisURLText += changefreqStart;
                   thisURLText += thisURL.changefreq;
                   thisURLText += changefreqEnd;
                }
                if(thisURL.priority){
                   thisURLText += priorityStart;
                   thisURLText += thisURL.priority;
                   thisURLText += priorityEnd;
                }
                thisURLText += urlEnd;
                if(thisURLText && thisURLText.length > 0){
                    xmlcontent += thisURLText;
                }
            }else{
                console.log("Something went very wrong!! at " + index + " | " + thisURL);
            }
        });
        xmlcontent += urlsetEnd;

    }else{

    }
    //console.log(xmlcontent);
    return xmlcontent;
};
router.post('/generateBlogSitemap', function(req, res){
    console.log('Generating Blog Sitemap');
    var prefix = "https://www.exambazaar.com/blogpost/";
    var urlobjs = [];
    var blogLastMod = moment().format("YYYY-MM-DD");
    var blogurlobj = {
        urlslug: "https://www.exambazaar.com/blog",
        changefreq: "hourly",
        priority: 0.9,
        lastmod: blogLastMod,
    };
    urlobjs.push(blogurlobj);

    var examBlogs = [
        "https://www.exambazaar.com/blog/jee-main",
        "https://www.exambazaar.com/blog/ssc-cgl",
        "https://www.exambazaar.com/blog/ssc-chsl",
        "https://www.exambazaar.com/blog/sbi-po",
        "https://www.exambazaar.com/blog/bank-po",
        "https://www.exambazaar.com/blog/nda",
        "https://www.exambazaar.com/blog/neet",
        "https://www.exambazaar.com/blog/ca-cpt",
        "https://www.exambazaar.com/blog/ntse",
        "https://www.exambazaar.com/blog/cat",
        "https://www.exambazaar.com/blog/clat",
        "https://www.exambazaar.com/blog/sat",
        "https://www.exambazaar.com/blog/gmat",
        "https://www.exambazaar.com/blog/cfa",
        "https://www.exambazaar.com/blog/ibps-po",
        "https://www.exambazaar.com/blog/ias",
        "https://www.exambazaar.com/blog/jee-advanced",
        "https://www.exambazaar.com/blog/ca-ipcc",
        "https://www.exambazaar.com/blog/ca-final",
        "https://www.exambazaar.com/blog/kvpy",
        "https://www.exambazaar.com/blog/bank-clerk",
        "https://www.exambazaar.com/blog/ifs",
        "https://www.exambazaar.com/blog/cds",
        "https://www.exambazaar.com/blog/lic-do",
        "https://www.exambazaar.com/blog/nata",
        "https://www.exambazaar.com/blog/aiims",
        "https://www.exambazaar.com/blog/xat",
        "https://www.exambazaar.com/blog/lsat",
        "https://www.exambazaar.com/blog/gre",
        "https://www.exambazaar.com/blog/nift",
        "https://www.exambazaar.com/blog/ibps-clerk",
        "https://www.exambazaar.com/blog/cs-foundation",
        "https://www.exambazaar.com/blog/cs-executive",
        "https://www.exambazaar.com/blog/capf",
        "https://www.exambazaar.com/blog/ies-iss",
        "https://www.exambazaar.com/blog/indian-air-force-recruitment",
        "https://www.exambazaar.com/blog/indian-naval-academy-recruitment",
        "https://www.exambazaar.com/blog/gic",
        "https://www.exambazaar.com/blog/bitsat",
        "https://www.exambazaar.com/blog/snap",
        "https://www.exambazaar.com/blog/ailet",
        "https://www.exambazaar.com/blog/ielts",
        "https://www.exambazaar.com/blog/toefl",
        "https://www.exambazaar.com/blog/rbi-grade-b",
        "https://www.exambazaar.com/blog/cs-professional",
        "https://www.exambazaar.com/blog/neet-pg",
        "https://www.exambazaar.com/blog/aiims-pg",
        "https://www.exambazaar.com/blog/ras",
        "https://www.exambazaar.com/blog/ssc-cpo",
        "https://www.exambazaar.com/blog/gate",
        "https://www.exambazaar.com/blog/frm",
        "https://www.exambazaar.com/blog/ssc-je",
        "https://www.exambazaar.com/blog/afcat",
        "https://www.exambazaar.com/blog/cmat",
        "https://www.exambazaar.com/blog/rbi-assistant",
    ];

    examBlogs.forEach(function(thisExam, eindex){
        blogurlobj = {
            urlslug: thisExam,
            changefreq: "daily",
            priority: 0.85,
            lastmod: blogLastMod,
        };
        urlobjs.push(blogurlobj);
    });

    var allBlogs = blogpost.find({active: true}, {urlslug:1, m_urlslug: 1, _published: 1}, function(err, allBlogs) {
    if (!err){
        var nBlogs = allBlogs.length;
        var counter = 0;
        allBlogs.forEach(function(thisBlog, index){
            var slug = thisBlog.m_urlslug;
            if(slug){
                var thisURL = prefix + slug;
                var newblogposturlobj = {
                    urlslug: thisURL,
                    changefreq: "weekly",
                    priority: 0.8,
                };
                var thisLastMod = null;
                if(thisBlog._published){
                    thisLastMod = moment(thisBlog._published).format("YYYY-MM-DD");
                }
                if(thisLastMod){
                    newblogposturlobj.lastmod = thisLastMod;
                }
                urlobjs.push(newblogposturlobj);
            }else{
                slug = thisBlog.urlslug;
                if(slug){
                    var thisURL = prefix + slug;
                    var newblogposturlobj = {
                        urlslug: thisURL,
                        changefreq: "weekly",
                        priority: 0.8,
                    };
                    var thisLastMod = null;
                    if(thisBlog._published){
                        thisLastMod = moment(thisBlog._published).format("YYYY-MM-DD");
                    }
                    if(thisLastMod){
                        newblogposturlobj.lastmod = thisLastMod;
                    }
                    urlobjs.push(newblogposturlobj);
                }
            }

            counter+= 1;
            if(counter == nBlogs){
                //console.log(urlobjs);
                var sitemapText = generateXML(urlobjs);
                if(sitemapText && sitemapText.length > 0){
                    //console.log(sitemapText);
                    var jsonPath = path.join(__dirname, '..', 'public','sitemaps', 'blogsitemap.xml');
                    fs.writeFile(jsonPath, sitemapText, function(err) {
                        if(err) {
                            console.log(err);
                        }
                        console.log("blogsitemap.xml file was saved!");
                    });
                    res.json(true);
                }else{
                    res.json(false);
                }

            }
        });
    } else {throw err;}
    });
});

router.post('/currentAffairsSitemap', function(req, res){
    console.log('Generating Current Affairs Sitemap');
    var prefix = "https://www.exambazaar.com/blogpost/";
    var urlobjs = [];
    var blogLastMod = moment().format("YYYY-MM-DD");
    var blogurlobj = {
        urlslug: "https://www.exambazaar.com/blog",
        changefreq: "hourly",
        priority: 0.9,
        lastmod: blogLastMod,
    };
    urlobjs.push(blogurlobj);

    var examBlogs = [
        "https://www.exambazaar.com/blog/jee-main",
        "https://www.exambazaar.com/blog/ssc-cgl",
        "https://www.exambazaar.com/blog/ssc-chsl",
        "https://www.exambazaar.com/blog/sbi-po",
        "https://www.exambazaar.com/blog/bank-po",
        "https://www.exambazaar.com/blog/nda",
        "https://www.exambazaar.com/blog/neet",
        "https://www.exambazaar.com/blog/ca-cpt",
        "https://www.exambazaar.com/blog/ntse",
        "https://www.exambazaar.com/blog/cat",
        "https://www.exambazaar.com/blog/clat",
        "https://www.exambazaar.com/blog/sat",
        "https://www.exambazaar.com/blog/gmat",
        "https://www.exambazaar.com/blog/cfa",
        "https://www.exambazaar.com/blog/ibps-po",
        "https://www.exambazaar.com/blog/ias",
        "https://www.exambazaar.com/blog/jee-advanced",
        "https://www.exambazaar.com/blog/ca-ipcc",
        "https://www.exambazaar.com/blog/ca-final",
        "https://www.exambazaar.com/blog/kvpy",
        "https://www.exambazaar.com/blog/bank-clerk",
        "https://www.exambazaar.com/blog/ifs",
        "https://www.exambazaar.com/blog/cds",
        "https://www.exambazaar.com/blog/lic-do",
        "https://www.exambazaar.com/blog/nata",
        "https://www.exambazaar.com/blog/aiims",
        "https://www.exambazaar.com/blog/xat",
        "https://www.exambazaar.com/blog/lsat",
        "https://www.exambazaar.com/blog/gre",
        "https://www.exambazaar.com/blog/nift",
        "https://www.exambazaar.com/blog/ibps-clerk",
        "https://www.exambazaar.com/blog/cs-foundation",
        "https://www.exambazaar.com/blog/cs-executive",
        "https://www.exambazaar.com/blog/capf",
        "https://www.exambazaar.com/blog/ies-iss",
        "https://www.exambazaar.com/blog/indian-air-force-recruitment",
        "https://www.exambazaar.com/blog/indian-naval-academy-recruitment",
        "https://www.exambazaar.com/blog/gic",
        "https://www.exambazaar.com/blog/bitsat",
        "https://www.exambazaar.com/blog/snap",
        "https://www.exambazaar.com/blog/ailet",
        "https://www.exambazaar.com/blog/ielts",
        "https://www.exambazaar.com/blog/toefl",
        "https://www.exambazaar.com/blog/rbi-grade-b",
        "https://www.exambazaar.com/blog/cs-professional",
        "https://www.exambazaar.com/blog/neet-pg",
        "https://www.exambazaar.com/blog/aiims-pg",
        "https://www.exambazaar.com/blog/ras",
        "https://www.exambazaar.com/blog/ssc-cpo",
        "https://www.exambazaar.com/blog/gate",
        "https://www.exambazaar.com/blog/frm",
        "https://www.exambazaar.com/blog/ssc-je",
        "https://www.exambazaar.com/blog/afcat",
        "https://www.exambazaar.com/blog/cmat",
        "https://www.exambazaar.com/blog/rbi-assistant",
    ];

    examBlogs.forEach(function(thisExam, eindex){
        blogurlobj = {
            urlslug: thisExam,
            changefreq: "daily",
            priority: 0.85,
            lastmod: blogLastMod,
        };
        urlobjs.push(blogurlobj);
    });

    var allBlogs = blogpost.find({active: true}, {urlslug:1, m_urlslug: 1, _published: 1}, function(err, allBlogs) {
    if (!err){
        var nBlogs = allBlogs.length;
        var counter = 0;
        allBlogs.forEach(function(thisBlog, index){
            var slug = thisBlog.m_urlslug;
            if(slug){
                var thisURL = prefix + slug;
                var newblogposturlobj = {
                    urlslug: thisURL,
                    changefreq: "weekly",
                    priority: 0.8,
                };
                var thisLastMod = null;
                if(thisBlog._published){
                    thisLastMod = moment(thisBlog._published).format("YYYY-MM-DD");
                }
                if(thisLastMod){
                    newblogposturlobj.lastmod = thisLastMod;
                }
                urlobjs.push(newblogposturlobj);
            }else{
                slug = thisBlog.urlslug;
                if(slug){
                    var thisURL = prefix + slug;
                    var newblogposturlobj = {
                        urlslug: thisURL,
                        changefreq: "weekly",
                        priority: 0.8,
                    };
                    var thisLastMod = null;
                    if(thisBlog._published){
                        thisLastMod = moment(thisBlog._published).format("YYYY-MM-DD");
                    }
                    if(thisLastMod){
                        newblogposturlobj.lastmod = thisLastMod;
                    }
                    urlobjs.push(newblogposturlobj);
                }
            }

            counter+= 1;
            if(counter == nBlogs){
                //console.log(urlobjs);
                var sitemapText = generateXML(urlobjs);
                if(sitemapText && sitemapText.length > 0){
                    //console.log(sitemapText);
                    var jsonPath = path.join(__dirname, '..', 'public','sitemaps', 'blogsitemap.xml');
                    fs.writeFile(jsonPath, sitemapText, function(err) {
                        if(err) {
                            console.log(err);
                        }
                        console.log("blogsitemap.xml file was saved!");
                    });
                    res.json(true);
                }else{
                    res.json(false);
                }

            }
        });
    } else {throw err;}
    });
});

router.post('/generateExamSitemap', function(req, res){
    console.log('Generating Exam Pages Sitemap');
    var prefix = "https://www.exambazaar.com/exam/";
    var urlobjs = [];

    var allExams = exam.find({active: true, urlslug: {$exists: true}}, {urlslug:1}, function(err, allExams) {
    if (!err){
        var nExams = allExams.length;
        var counter = 0;
        allExams.forEach(function(thisExam, index){
            var thisURL = prefix + thisExam.urlslug;
            var newblogposturlobj = {
                urlslug: thisURL,
                changefreq: "daily",
                priority: 0.6,
            };
            urlobjs.push(newblogposturlobj);
            counter+= 1;
            if(counter == nExams){
                var sitemapText = generateXML(urlobjs);
                if(sitemapText && sitemapText.length > 0){
                    var jsonPath = path.join(__dirname, '..', 'public','sitemaps', 'examsitemap.xml');

                    fs.writeFile(jsonPath, sitemapText, function(err) {
                    if(err) {
                        console.log(err);
                    }

                    console.log("examsitemap.xml file was saved!");
                    });
                    res.json(true);
                }else{
                    res.json(false);
                }

            }
        });
    } else {throw err;}
    });
});

router.post('/generateQPSitemap', function(req, res){
    console.log('Generating Question Papers Sitemap');
    var prefix = "https://www.exambazaar.com/qp/";
    var urlobjs = [];
    var qpurlobj = {
        urlslug: "https://www.exambazaar.com/previous-year-question-papers-for-competitive-exams",
        changefreq: "daily",
        priority: 1,
    };
    urlobjs.push(qpurlobj);
    var distinctTests = test.distinct( "exam", {official: true, simulationactive: true},function(err, distinctTests){
    if (!err){
        var allExams = exam.find({active: true, _id: {$in: distinctTests}, question_papers_urlslug: {$exists: true}}, {question_papers_urlslug:1}, function(err, allExams) {
        if (!err){
            var nExams = allExams.length;
            var counter = 0;
            allExams.forEach(function(thisExam, index){
                var thisURL = prefix + thisExam.question_papers_urlslug;
                var newqpurlobj = {
                    urlslug: thisURL,
                    changefreq: "daily",
                    priority: 0.9,
                };
                urlobjs.push(newqpurlobj);
                counter+= 1;
                if(counter == nExams){
                    var sitemapText = generateXML(urlobjs);
                    if(sitemapText && sitemapText.length > 0){
                        var jsonPath = path.join(__dirname, '..', 'public','sitemaps', 'qpsitemap.xml');

                        fs.writeFile(jsonPath, sitemapText, function(err) {
                        if(err) {
                            console.log(err);
                        }

                        console.log("qpsitemap.xml file was saved!");
                        });
                        res.json(true);
                    }else{
                        res.json(false);
                    }

                }
            });
        } else {throw err;}
        });

    } else {throw err;}
    });

});

router.post('/generateTopCoachingSitemap', function(req, res){
    console.log('Generating Top Coaching Sitemap');
    var prefix = "https://www.exambazaar.com/reviews/";
    var urlobjs = [];
    var qpurlobj = {
        urlslug: "https://www.exambazaar.com/aroundme",
        changefreq: "daily",
        priority: 1,
    };
    urlobjs.push(qpurlobj);

    var allExpertReviews = blogpost.aggregate(
        [
            {$match: {active: true, blogSeries: 'Expert Reviews'} },
            {$unwind:"$exams"},
            {"$group": {
                "_id": { _id: "$_id"},
                exams: { $addToSet: "$exams" },
            }},

        ],function(err, allExpertReviews) {
        if (!err){
            var allExamIds = [];
            allExpertReviews.forEach(function(thisReview, rindex){
                var thisRExams = thisReview.exams;
                thisReview.exams.forEach(function(thisExam, eindex){
                    if(allExamIds.indexOf(thisExam.toString())){
                        allExamIds.push(thisExam.toString());
                    }
                });
            });

            var allExams = exam.find({active: true, _id: {$in: allExamIds}, top_coaching_urlslug: {$exists: true}}, {top_coaching_urlslug:1}, function(err, allExams) {
            if (!err){
                var nExams = allExams.length;
                var counter = 0;
                allExams.forEach(function(thisExam, index){
                    var thisURL = prefix + thisExam.top_coaching_urlslug;
                    var newqpurlobj = {
                        urlslug: thisURL,
                        changefreq: "daily",
                        priority: 0.9,
                    };
                    urlobjs.push(newqpurlobj);
                    counter+= 1;
                    if(counter == nExams){
                        var sitemapText = generateXML(urlobjs);
                        if(sitemapText && sitemapText.length > 0){
                            var jsonPath = path.join(__dirname, '..', 'public','sitemaps', 'topcoachingssitemap.xml');

                            fs.writeFile(jsonPath, sitemapText, function(err) {
                            if(err) {
                                console.log(err);
                            }

                            console.log("topcoachingssitemap.xml file was saved!");
                            });
                            res.json(true);
                        }else{
                            res.json(false);
                        }

                    }
                });
            } else {throw err;}
            });
        }
    });

});

router.post('/generateN5Sitemap', function(req, res){
    console.log('Generating N5 Pages Sitemap');
    var prefix = "https://www.exambazaar.com/c/";

    var limit = 5000;
    var skip = 0;
    res.json(true);
    var iter = 1;
    var refreshIntervalId = setInterval(function(){
    skip = limit * (iter - 1);
    console.log('Iteration number: ' + iter + " | Limit: " + limit + " | Skip: " + skip);

    var fileName = "n5sitemap" + iter + ".xml";
    var allCoachings = coaching.find({type: 'Coaching', nameslug: {$exists: true}, areaslug: {$exists: true}, sublocality: {$exists: true}, seo: {$exists: true}}, {nameslug:1, areaslug: 1}, function(err, allCoachings) {
    if (!err){
        var urlobjs = [];
        var nCoachings = allCoachings.length;
        if(nCoachings == 0){

            clearInterval(refreshIntervalId);
            //res.json(true);
        }
        var counter = 0;
        console.log("There are a total of " + nCoachings + " coachings");
        allCoachings.forEach(function(thisCoaching, index){
            var thisURL = prefix + thisCoaching.nameslug + "/" + thisCoaching.areaslug;
            var newcoachingposturlobj = {
                urlslug: thisURL,
                changefreq: "daily",
                priority: 0.7,
            };
            urlobjs.push(newcoachingposturlobj);
            counter+= 1;
            if(counter == nCoachings){
                var sitemapText = generateXML(urlobjs);
                if(sitemapText && sitemapText.length > 0){
                    var jsonPath = path.join(__dirname, '..', 'public','sitemaps', fileName);

                    fs.writeFile(jsonPath, sitemapText, function(err) {
                    if(err) {
                        console.log(err);
                    }

                    console.log(fileName + " file was saved!");
                    });
                    //res.json(true);
                }else{
                    //res.json(false);
                }

            }
        });
    } else {throw err;}
    }).limit(limit).skip(skip);
    iter += 1;
    }, 30000);
});

router.post('/generateN4Sitemap', function(req, res){
    console.log('Generating N4 Pages Sitemap');
    var prefix = "https://www.exambazaar.com/";
    res.json(true);
    var urlobjs = [];
    var allCities = city.find({active: true, count: {$gte: 2}}, {name: 1, count : 1}, function(err, allCities) {
    if (!err && allCities && allCities.length > 0){
        var allExams = exam.find({active: true}, {_id: 1, coaching_page_slug: 1}, function(err, allExams) {
        if (!err && allExams && allExams.length > 0){

            var nTotal = allCities.length * allExams.length;
            var counter = 0;
            var aCounter = 0;
            var nextLevel = 10;
            allCities.forEach(function(thisCity, cindex){
                allExams.forEach(function(thisExam, eindex){

                coaching.count({city: thisCity.name, exams: thisExam._id, nameslug: {$exists: true}, areaslug: {$exists: true}, disabled:false, sublocality: {$exists: true}, type: 'Coaching'}, function(err, thisCityExamCount) {
                if (!err){
                    counter += 1;
                    if(thisCityExamCount >= 2){

                        var coachingPageSlug = thisExam.coaching_page_slug;
                        var citySlug = slugify(thisCity.name);
                        var n4Slug = coachingPageSlug + "/" + citySlug;
                        aCounter += 1;
                        var thisURL = prefix + n4Slug;
                        var percentageDone = Math.round(10 * 100 * counter / nTotal) / 10;
                        if(percentageDone > nextLevel){
                            console.log(percentageDone + " percentage done!");
                            nextLevel += 10;
                        }
                        //console.log(aCounter + ". Adding " + thisURL);
                        var newcoachingcity = {
                            urlslug: thisURL,
                            changefreq: "daily",
                            priority: 0.95,
                        };
                        urlobjs.push(newcoachingcity);
                    }
                    if(counter == nTotal){
                        //console.log("I am here!!");
                        var sitemapText = generateXML(urlobjs);
                        if(sitemapText && sitemapText.length > 0){
                            var jsonPath = path.join(__dirname, '..', 'public','sitemaps', "n4sitemap.xml");

                            fs.writeFile(jsonPath, sitemapText, function(err) {
                            if(err) {
                                console.log(err);
                            }

                            console.log("n4sitemap.xml file was saved!");
                            });
                            //res.json(true);
                        }else{
                            //res.json(false);
                        }
                    }

                } else {throw err;}
                });

                });
            });

        }else{
            res.json(false);
        }
        });

    }else{
        res.json(false);
    }
    });
});

router.post('/blogrss', function(req, res) {
    //var mainblog = "https://www.exambazaar.com/blog";
    var prefix = "https://www.exambazaar.com/blogpost";
    var separator = "/";
    var urls = [];
    //urls.push(mainblog);
    //urls.push(prefix);
    var allBlogs = blogpost.find({active: true}, {title:1, seoDescription:1, _published:1, urlslug:1}, function(err, allBlogs) {
    if (!err){
        var nBlogs = allBlogs.length;
        var counter = 0;
        allBlogs.forEach(function(thisBlog, index){
            var thisURL = prefix + separator + thisBlog.urlslug;
            thisBlog.urlslug = thisURL;
            urls.push(thisBlog);
            counter+= 1;
            if(counter == nBlogs){
                res.json(urls);
            }
        });
    } else {throw err;}
    });
});

router.post('/exams', function(req, res) {
    //var mainblog = "https://www.exambazaar.com/blog";
    var prefix = "https://www.exambazaar.com/exam";
    var separator = "/";
    var urls = [];
    var allExams = exam.find({active: true}, {name:1}, function(err, allExams) {
    if (!err){
        var nExams = allExams.length;
        var counter = 0;
        allExams.forEach(function(thisExam, index){
            var examName = encodeURIComponent(thisExam.name);
            var thisURL = prefix + separator + examName;
            urls.push(thisURL);
            counter+= 1;
            if(counter == nExams){
                res.json(urls);
            }
        });
    } else {throw err;}
    });
});

router.post('/p2', function(req, res) {
    var prefix = "https://www.exambazaar.com/stream";
    var separator = "/";
    var urls = [];
    var allStreams = stream.find({active: {$ne: false}}, {name:1}, function(err, allStreams) {
    if (!err){
        var nStreams = allStreams.length;
        var scounter = 0;
        allStreams.forEach(function(thisStream, sindex){
            var thisStreamId = thisStream._id;
            var allExams = exam.find({active: {$ne: false}, stream: thisStreamId}, {name:1}, function(err, allExams) {
            if (!err){
                scounter += 1;
                var nExams = allExams.length;
                var ecounter = 0;
                allExams.forEach(function(thisExam, eindex){
                    var thisURL = prefix + separator + encodeURIComponent(thisStream.name) + separator + encodeURIComponent(thisExam.name);
                    urls.push(thisURL);
                    ecounter+= 1;
                    if(ecounter == nExams && scounter == nStreams){
                        res.json(urls);
                    }
                });
            } else {throw err;}
            });

        });
    } else {throw err;}
    });
});
router.post('/p3', function(req, res) {
    var prefix = "https://www.exambazaar.com/stream";
    var separator = "/";
    var urls = [];

    var allCities = coaching.distinct( ("city"),function(err, allCities) {
    if (!err){
        var nCities = allCities.length;
        var cCounter = 0;

        var allStreams = stream.find({active: {$ne: false}, name:{$ne: 'other'}}, {name:1}, function(err, allStreams) {
        if (!err){
            var nStreams = allStreams.length;
            var scounter = 0;
            allStreams.forEach(function(thisStream, sindex){
                var thisStreamId = thisStream._id;
                var allExams = exam.find({active: {$ne: false}, stream: thisStreamId}, {name:1}, function(err, allExams) {
                if (!err){
                    scounter += 1;
                    var nExams = allExams.length;
                    var ecounter = 0;
                    allExams.forEach(function(thisExam, eindex){
                        ecounter+= 1;
                        allCities.forEach(function(thisCity, cindex){
                            var thisURL = prefix + separator + encodeURIComponent(thisStream.name) + separator + encodeURIComponent(thisExam.name)+ separator + encodeURIComponent(thisCity);
                            urls.push(thisURL);
                            //console.log(thisURL);

                            if(ecounter == nExams && scounter == nStreams && cindex == nCities - 1){
                                res.json(urls);
                            }

                        });

                    });
                } else {throw err;}
                });

            });
        } else {throw err;}
        });

    } else {throw err;}
    });
});

router.post('/p3Aggregate', function(req, res) {
    var cityExamCounts = [];
    var allExams = exam.find({active: {$ne: false}}, {name:1}, function(err, allExams) {
        var nExams = allExams.length;
        var eCounter = 0;
        if (!err){
            allExams.forEach(function(thisExam, eindex){
                var thisExamId = thisExam._id;
                var groupNames = coaching.aggregate(
                [
                    {$match: {disabled: false, exams: thisExamId, type:'Coaching'} },
                    {"$group": { "_id": { city: "$city" }, count:{$sum:1} } },
                    {$sort:{"count":-1}}
                ],function(err, groupNames) {
                if (!err){
                    groupNames.forEach(function(thisPair, index){
                        var newCityExamCount = {
                            exam: encodeURIComponent(thisExam.name),
                            city: encodeURIComponent(thisPair._id.city),
                            count: thisPair.count,
                        };
                        cityExamCounts.push(newCityExamCount);
                    });
                    eCounter += 1;
                    if(eCounter == nExams){
                        res.json(cityExamCounts);
                    }
                } else {throw err;}
                });
            });
        } else {throw err;}
    });
});

router.post('/p5', function(req, res) {
    var prefix = "https://www.exambazaar.com/group";
    var separator = "/";
    var urls = [];
    var examStreams = [];

    var allStreams = stream.find({active: {$ne: false}, name:{$ne: 'other'}}, {name:1}, function(err, allStreams) {
    if (!err){
        var nStreams = allStreams.length;
        var scounter = 0;
        allStreams.forEach(function(thisStream, sindex){
            var thisStreamId = thisStream._id;
            var allExams = exam.find({active: {$ne: false}, stream: thisStreamId}, {name:1}, function(err, allExams) {
            if (!err){
            scounter += 1;
            var nExams = allExams.length;
            var ecounter = 0;
            allExams.forEach(function(thisExam, eindex){
            ecounter+= 1;
            var newExamStream = {
                examId: thisExam._id,
                exam: thisExam.name,
                stream: thisStream.name,
            };
            examStreams.push(newExamStream);
            if(ecounter == nExams && scounter == nStreams){
                var examStreamIds = examStreams.map(function(a) {return a.examId.toString();});
                //console.log(examStreamIds);
                var groupNames = coaching.aggregate(
                [
                    {$match: {disabled: false, exams: {$exists: true}} },
                    {"$group": { "_id": { groupName: "$groupName", city: "$city" }, exams: { $addToSet: "$exams" } } },
                    {$sort:{"count":-1}}
                ],function(err, groupNames) {
                if (!err){
                    var gCounter = 0;
                    var nGroups = groupNames.length;
                    groupNames.forEach(function(thisGroup, gindex){
                        gCounter += 1;
                        var coachingName = thisGroup._id.groupName;
                        var cityName = thisGroup._id.city;
                        var thisExamIds = thisGroup.exams[0];
                        var nExamIds = thisExamIds.length;
                        var exCounter = 0;
                        thisExamIds.forEach(function(thisExamId, eindex){
                            exCounter += 1;
                            var exsIndex = examStreamIds.indexOf(thisExamId.toString());
                            //console.log(thisExamId + " " + exsIndex);
                            if(exsIndex != -1){
                                var examStreamPair = examStreams[exsIndex];
                                var thisURL = prefix + separator + encodeURIComponent(examStreamPair.stream) + separator + encodeURIComponent(examStreamPair.exam)+ separator + encodeURIComponent(cityName) + separator + encodeURIComponent(coachingName);
                                urls.push(thisURL);
                                //console.log(thisURL);
                            }
                            //var thisURL = prefix + separator + encodeURIComponent(thisStream.name) + separator + encodeURIComponent(thisExam.name)+ separator + encodeURIComponent(thisCity) + separator + encodeURIComponent(thisGroup._id.groupName);
                            //urls.push(thisURL);
                            //console.log(thisURL);
                            //console.log(thisGroup);

                            if(gCounter == nGroups && exCounter == nExamIds){
                                res.json(urls);
                            }

                        });

                    });
                } else {throw err;}
                });

            }

            });
            } else {throw err;}
            });

        });
    } else {throw err;}
    });

});

module.exports = router;


text:
'use strict';

/**
 * @ngdoc function
 * @name paybookApp.controller:RegisterCtrl
 * @description
 * # RegisterCtrl
 * Controller of the paybookApp
 */
angular.module('paybookApp')
  .controller('RegisterCtrl', function ($scope, $http, $location) {
    $scope.registerForm = {
      name: "",
      email: ""
    };

    $scope.error = false;

    $scope.registerUser = function () {
      var url = "/api/user/register";

      $http.post(url, $scope.registerForm).
        success(function (data) {
          console.log('success');
          $location.path("/profile")

        }).
        error(function (err) {
          $scope.error = true;
        });

    };
  });

redpajama_set_name: RedPajamaGithub


text:
/*jslint nomen: true, plusplus: false, sloppy: true, white:true*/
/*jshint nomen: false, curly: true, plusplus: false, expr:true, undef:true, newcap:true, latedef:true, camelcase:true  */
/*global module: false, iScroll:false, setTimeout: false, document:false, WebKitCSSMatrix:false, _: false, Backbone: false, backbone: false, $: false, define: false, require: false, console: false, window:false */

var _ = require('underscore');
var	Forbidden = require('custom-errors').request.Forbidden;

var checkHost = function (actual, allowed) {

	var allowed = _.any(allowed, function (domain) {
		var re = new RegExp(domain);
		return re.test(actual);
	});
	return allowed;
};

var validateHostsOrigins = function (req, allowedHosts, allowedOrigins, opts) {

	// as jquery won't allow you to set origin when simulating jsonp requests
	var originSecret = opts && opts.originSecret;
	var checkOrigin  = req.query[originSecret] || checkHost(req.headers.origin, allowedOrigins);
	var checkReferer = (opts && opts.dontCheckReferer) || checkHost(req.headers.referer, allowedHosts);

	// origin only set on CORS json requests NOT jsonPs
	return req.is('json') ? checkReferer && checkOrigin : checkReferer;
};

var allowXOrigin = function (allowedHosts, allowedOrigins, opts) {
	var returnXOrigin = function (req, res, next) {

	    if (validateHostsOrigins(req, allowedHosts, allowedOrigins, opts)) {

	    	if (req.is('json')) {
				// chrome only allows a single origin header
				// http://stackoverflow.com/questions/1653308/access-control-allow-origin-multiple-origin-domains
				// res.header('Access-Control-Allow-Origin', "*");
				res.header('Access-Control-Allow-Origin', req.headers.origin);
				res.header('Access-Control-Allow-Credentials', true);
				res.header('Access-Control-Allow-Methods', opts.httpMethods || 'GET,PUT,POST,DELETE');
				res.header('Access-Control-Allow-Headers', 'Content-Type, X-Requested-With');
	    	}

			next();

		} else {
			next(new Forbidden('host or referer not allowed'))
			// res.send(403, 'Forbidden');
		}
	};
	return returnXOrigin;
};

module.exports = {
	checkHost: checkHost,
	validateHostsOrigins: validateHostsOrigins,
	allowXOrigin: allowXOrigin
};


text:
using System;

namespace RecycleBin.TextTables
{
   /// <summary>
   /// Parser interface.
   /// </summary>
   /// <remarks>
   /// This interface may not be implemented.
   /// </remarks>
   public interface IParser
   {
      /// <summary>
      /// Parses the input value as object representation.
      /// </summary>
      /// <param name="value">The string to parse.</param>
      /// <param name="provider">The provider.</param>
      /// <returns>The result.</returns>
      object Parse(string value, IFormatProvider provider);
   }

   /// <summary>
   /// Formatter interface.
   /// </summary>
   /// <remarks>
   /// This interface may not be implemented.
   /// </remarks>
   public interface IFormatter
   {
      /// <summary>
      /// Formats the input value as string representation.
      /// </summary>
      /// <param name="value">The object to format.</param>
      /// <param name="provider">The provider.</param>
      /// <returns>The string representation.</returns>
      string Format(object value, IFormatProvider provider);
   }
}


text:
package reactor.netty.http.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.haproxy.HAProxyMessageDecoder;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler;
import io.netty.handler.codec.http2.Http2CodecUtil;
import io.netty.handler.codec.http2.Http2FrameCodec;
import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
import io.netty.handler.codec.http2.Http2FrameLogger;
import io.netty.handler.codec.http2.Http2MultiplexHandler;
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
import io.netty.util.AsciiString;
import reactor.core.publisher.Mono;
import reactor.netty.ChannelPipelineConfigurer;
import reactor.netty.Connection;
import reactor.netty.ConnectionObserver;
import reactor.netty.NettyPipeline;
import reactor.netty.ReactorNetty;
import reactor.netty.channel.AbstractChannelMetricsHandler;
import reactor.netty.channel.ChannelMetricsRecorder;
import reactor.netty.channel.ChannelOperations;
import reactor.netty.http.Http2SettingsSpec;
import reactor.netty.http.HttpProtocol;
import reactor.netty.http.HttpResources;
import reactor.netty.http.logging.HttpMessageLogFactory;
import reactor.netty.http.logging.ReactorNettyHttpMessageLogFactory;
import reactor.netty.http.server.logging.AccessLog;
import reactor.netty.http.server.logging.AccessLogArgProvider;
import reactor.netty.http.server.logging.AccessLogHandlerFactory;
import reactor.netty.resources.LoopResources;
import reactor.netty.tcp.SslProvider;
import reactor.netty.transport.ServerTransportConfig;
import reactor.netty.transport.logging.AdvancedByteBufFormat;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.annotation.Nullable;

import java.net.SocketAddress;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Supplier;

import static reactor.netty.ReactorNetty.ACCESS_LOG_ENABLED;
import static reactor.netty.ReactorNetty.format;
import static reactor.netty.http.server.HttpServerFormDecoderProvider.DEFAULT_FORM_DECODER_SPEC;

/**
 * Encapsulate all necessary configuration for HTTP server transport. The public API is read-only.
 *
 * @author Stephane Maldini
 * @author Violeta Georgieva
 * @author Andrey Shlykov
 */
public final class HttpServerConfig extends ServerTransportConfig<HttpServerConfig> {

	/**
	 * Return the configured compression predicate or null.
	 *
	 * @return the configured compression predicate or null
	 */
	@Nullable
	public BiPredicate<HttpServerRequest, HttpServerResponse> compressPredicate() {
		return compressPredicate;
	}

	/**
	 * Return the configured {@link ServerCookieDecoder} or the default {@link ServerCookieDecoder#STRICT}.
	 *
	 * @return the configured {@link ServerCookieDecoder} or the default {@link ServerCookieDecoder#STRICT}
	 * @deprecated as of 1.1.0. This will be removed in 2.0.0 as Netty 5 supports only strict validation.
	 */
	@Deprecated
	public ServerCookieDecoder cookieDecoder() {
		return cookieDecoder;
	}

	/**
	 * Return the configured {@link ServerCookieEncoder} or the default {@link ServerCookieEncoder#STRICT}.
	 *
	 * @return the configured {@link ServerCookieEncoder} or the default {@link ServerCookieEncoder#STRICT}
	 * @deprecated as of 1.1.0. This will be removed in 2.0.0 as Netty 5 supports only strict validation.
	 */
	@Deprecated
	public ServerCookieEncoder cookieEncoder() {
		return cookieEncoder;
	}

	/**
	 * Return the configured HTTP request decoder options or the default.
	 *
	 * @return the configured HTTP request decoder options or the default
	 */
	public HttpRequestDecoderSpec decoder() {
		return decoder;
	}

	/**
	 * Return the configured HTTP form decoder or the default.
	 *
	 * @return the configured HTTP form decoder or the default
	 * @since 1.0.11
	 */
	public HttpServerFormDecoderProvider formDecoderProvider() {
		return formDecoderProvider;
	}

	/**
	 * Return the HTTP/2 configuration
	 *
	 * @return the HTTP/2 configuration
	 */
	public Http2SettingsSpec http2SettingsSpec() {
		return http2Settings;
	}

	/**
	 * Return the configured idle timeout for the connection when it is waiting for an HTTP request or null.
	 *
	 * @return the configured idle timeout for the connection when it is waiting for an HTTP request or null
	 */
	@Nullable
	public Duration idleTimeout() {
		return idleTimeout;
	}

	/**
	 * Returns whether that {@link HttpServer} supports the {@code "Forwarded"} and {@code "X-Forwarded-*"}
	 * HTTP request headers for deriving information about the connection.
	 *
	 * @return true if that {@link HttpServer} supports the {@code "Forwarded"} and {@code "X-Forwarded-*"}
	 * HTTP request headers for deriving information about the connection
	 */
	public boolean isForwarded() {
		return forwardedHeaderHandler != null;
	}

	/**
	 * Returns true if that {@link HttpServer} secured via SSL transport
	 *
	 * @return true if that {@link HttpServer} secured via SSL transport
	 */
	public boolean isSecure() {
		return sslProvider != null;
	}

	/**
	 * The configured maximum number of HTTP/1.1 requests which can be served until the connection is closed by the server.
	 *
	 * @return the configured maximum number of HTTP/1.1 requests which can be served until the connection is closed by the server.
	 * @see HttpServer#maxKeepAliveRequests(int)
	 * @since 1.0.13
	 */
	public int maxKeepAliveRequests() {
		return maxKeepAliveRequests;
	}

	/**
	 * Compression is performed once response size exceeds the minimum compression size in bytes.
	 *
	 * @return the minimum compression size in bytes
	 */
	public int minCompressionSize() {
		return minCompressionSize;
	}

	/**
	 * Return the HTTP protocol to support. Default is {@link HttpProtocol#HTTP11}.
	 *
	 * @return the HTTP protocol to support
	 */
	public HttpProtocol[] protocols() {
		return protocols;
	}

	/**
	 * Return the supported type for the {@code "HAProxy proxy protocol"}.
	 * The default is {@link ProxyProtocolSupportType#OFF}.
	 *
	 * @return the supported type for the {@code "HAProxy proxy protocol"}
	 */
	public ProxyProtocolSupportType proxyProtocolSupportType() {
		return proxyProtocolSupportType;
	}

	/**
	 * Returns true if that {@link HttpServer} will redirect HTTP to HTTPS by changing
	 * the scheme only but otherwise leaving the port the same when SSL is enabled.
	 * This configuration is applicable only for HTTP/1.x.
	 *
	 * @return true if that {@link HttpServer} will redirect HTTP to HTTPS by changing
	 * the scheme only but otherwise leaving the port the same when SSL is enabled.
	 * This configuration is applicable only for HTTP/1.x.
	 */
	public boolean redirectHttpToHttps() {
		return redirectHttpToHttps;
	}

	/**
	 * Returns the current {@link SslProvider} if that {@link HttpServer} secured via SSL
	 * transport or null.
	 *
	 * @return the current {@link SslProvider} if that {@link HttpServer} secured via SSL
	 * transport or null
	 */
	@Nullable
	public SslProvider sslProvider() {
		return sslProvider;
	}

	/**
	 * Returns the configured function that receives the actual uri and returns the uri tag value
	 * that will be used for the metrics with {@link reactor.netty.Metrics#URI} tag
	 *
	 * @return the configured function that receives the actual uri and returns the uri tag value
	 * that will be used for the metrics with {@link reactor.netty.Metrics#URI} tag
	 */
	@Nullable
	public Function<String, String> uriTagValue() {
		return uriTagValue;
	}

	// Protected/Package private write API

	boolean                                                 accessLogEnabled;
	Function<AccessLogArgProvider, AccessLog>               accessLog;
	BiPredicate<HttpServerRequest, HttpServerResponse>      compressPredicate;
	ServerCookieDecoder                                     cookieDecoder;
	ServerCookieEncoder                                     cookieEncoder;
	HttpRequestDecoderSpec                                  decoder;
	HttpServerFormDecoderProvider                           formDecoderProvider;
	BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler;
	Http2SettingsSpec                                       http2Settings;
	HttpMessageLogFactory                                   httpMessageLogFactory;
	Duration                                                idleTimeout;
	BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>>
	                                                        mapHandle;
	int                                                     maxKeepAliveRequests;
	int                                                     minCompressionSize;
	HttpProtocol[]                                          protocols;
	int                                                     _protocols;
	ProxyProtocolSupportType                                proxyProtocolSupportType;
	boolean                                                 redirectHttpToHttps;
	SslProvider                                             sslProvider;
	Function<String, String>                                uriTagValue;

	HttpServerConfig(Map<ChannelOption<?>, ?> options, Map<ChannelOption<?>, ?> childOptions, Supplier<? extends SocketAddress> localAddress) {
		super(options, childOptions, localAddress);
		this.cookieDecoder = ServerCookieDecoder.STRICT;
		this.cookieEncoder = ServerCookieEncoder.STRICT;
		this.decoder = new HttpRequestDecoderSpec();
		this.formDecoderProvider = DEFAULT_FORM_DECODER_SPEC;
		this.httpMessageLogFactory = ReactorNettyHttpMessageLogFactory.INSTANCE;
		this.maxKeepAliveRequests = -1;
		this.minCompressionSize = -1;
		this.protocols = new HttpProtocol[]{HttpProtocol.HTTP11};
		this._protocols = h11;
		this.proxyProtocolSupportType = ProxyProtocolSupportType.OFF;
		this.accessLogEnabled = ACCESS_LOG;
	}

	HttpServerConfig(HttpServerConfig parent) {
		super(parent);
		this.accessLogEnabled = parent.accessLogEnabled;
		this.accessLog = parent.accessLog;
		this.compressPredicate = parent.compressPredicate;
		this.cookieDecoder = parent.cookieDecoder;
		this.cookieEncoder = parent.cookieEncoder;
		this.decoder = parent.decoder;
		this.formDecoderProvider = parent.formDecoderProvider;
		this.forwardedHeaderHandler = parent.forwardedHeaderHandler;
		this.http2Settings = parent.http2Settings;
		this.httpMessageLogFactory = parent.httpMessageLogFactory;
		this.idleTimeout = parent.idleTimeout;
		this.mapHandle = parent.mapHandle;
		this.maxKeepAliveRequests = parent.maxKeepAliveRequests;
		this.minCompressionSize = parent.minCompressionSize;
		this.protocols = parent.protocols;
		this._protocols = parent._protocols;
		this.proxyProtocolSupportType = parent.proxyProtocolSupportType;
		this.redirectHttpToHttps = parent.redirectHttpToHttps;
		this.sslProvider = parent.sslProvider;
		this.uriTagValue = parent.uriTagValue;
	}

	@Override
	protected LoggingHandler defaultLoggingHandler() {
		return LOGGING_HANDLER;
	}

	@Override
	protected LoopResources defaultLoopResources() {
		return HttpResources.get();
	}

	@Override
	protected ChannelMetricsRecorder defaultMetricsRecorder() {
		return MicrometerHttpServerMetricsRecorder.INSTANCE;
	}

	@Override
	protected ChannelPipelineConfigurer defaultOnChannelInit() {
		return super.defaultOnChannelInit()
		            .then(new HttpServerChannelInitializer(this));
	}

	@Override
	protected void loggingHandler(LoggingHandler loggingHandler) {
		super.loggingHandler(loggingHandler);
	}

	@Override
	protected void metricsRecorder(@Nullable Supplier<? extends ChannelMetricsRecorder> metricsRecorder) {
		super.metricsRecorder(metricsRecorder);
	}

	void protocols(HttpProtocol... protocols) {
		this.protocols = protocols;
		int _protocols = 0;

		for (HttpProtocol p : protocols) {
			if (p == HttpProtocol.HTTP11) {
				_protocols |= h11;
			}
			else if (p == HttpProtocol.H2) {
				_protocols |= h2;
			}
			else if (p == HttpProtocol.H2C) {
				_protocols |= h2c;
			}
		}
		this._protocols = _protocols;
	}

	Http2Settings http2Settings() {
		Http2Settings settings = Http2Settings.defaultSettings();

		if (http2Settings != null) {
			Long headerTableSize = http2Settings.headerTableSize();
			if (headerTableSize != null) {
				settings.headerTableSize(headerTableSize);
			}

			Integer initialWindowSize = http2Settings.initialWindowSize();
			if (initialWindowSize != null) {
				settings.initialWindowSize(initialWindowSize);
			}

			Long maxConcurrentStreams = http2Settings.maxConcurrentStreams();
			if (maxConcurrentStreams != null) {
				settings.maxConcurrentStreams(maxConcurrentStreams);
			}

			Integer maxFrameSize = http2Settings.maxFrameSize();
			if (maxFrameSize != null) {
				settings.maxFrameSize(maxFrameSize);
			}

			settings.maxHeaderListSize(http2Settings.maxHeaderListSize());

			Boolean pushEnabled = http2Settings.pushEnabled();
			if (pushEnabled != null) {
				settings.pushEnabled(pushEnabled);
			}
		}

		return settings;
	}

	static void addStreamHandlers(Channel ch,
			boolean accessLogEnabled,
			@Nullable Function<AccessLogArgProvider, AccessLog> accessLog,
			@Nullable BiPredicate<HttpServerRequest, HttpServerResponse> compressPredicate,
			ServerCookieDecoder decoder,
			ServerCookieEncoder encoder,
			HttpServerFormDecoderProvider formDecoderProvider,
			@Nullable BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler,
			HttpMessageLogFactory httpMessageLogFactory,
			ConnectionObserver listener,
			@Nullable BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>> mapHandle,
			@Nullable ChannelMetricsRecorder metricsRecorder,
			int minCompressionSize,
			ChannelOperations.OnSetup opsFactory,
			@Nullable Function<String, String> uriTagValue) {
		ChannelPipeline pipeline = ch.pipeline();
		if (accessLogEnabled) {
			pipeline.addLast(NettyPipeline.AccessLogHandler, AccessLogHandlerFactory.H2.create(accessLog));
		}
		pipeline.addLast(NettyPipeline.H2ToHttp11Codec, HTTP2_STREAM_FRAME_TO_HTTP_OBJECT)
		        .addLast(NettyPipeline.HttpTrafficHandler,
		                 new Http2StreamBridgeServerHandler(compressPredicate, decoder, encoder, formDecoderProvider,
		                         forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle));

		boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0;

		if (alwaysCompress) {
			pipeline.addLast(NettyPipeline.CompressionHandler, new SimpleCompressionHandler());
		}

		ChannelOperations.addReactiveBridge(ch, opsFactory, listener);

		if (metricsRecorder != null) {
			if (metricsRecorder instanceof HttpServerMetricsRecorder) {
				ChannelHandler handler;
				Channel parent = ch.parent();
				ChannelHandler existingHandler = parent.pipeline().get(NettyPipeline.HttpMetricsHandler);
				if (existingHandler != null) {
					// This use case can happen only in HTTP/2 clear text connection upgrade
					if (metricsRecorder instanceof MicrometerHttpServerMetricsRecorder) {
						parent.pipeline().replace(NettyPipeline.HttpMetricsHandler, NettyPipeline.ChannelMetricsHandler,
								new H2ChannelMetricsHandler(metricsRecorder));
					}
					else {
						parent.pipeline().remove(NettyPipeline.HttpMetricsHandler);
					}
					if (metricsRecorder instanceof MicrometerHttpServerMetricsRecorder) {
						handler = new MicrometerHttpServerMetricsHandler((MicrometerHttpServerMetricsHandler) existingHandler);
					}
					else if (metricsRecorder instanceof ContextAwareHttpServerMetricsRecorder) {
						handler = new ContextAwareHttpServerMetricsHandler((ContextAwareHttpServerMetricsHandler) existingHandler);
					}
					else {
						handler = new HttpServerMetricsHandler((HttpServerMetricsHandler) existingHandler);
					}
				}
				else {
					if (metricsRecorder instanceof MicrometerHttpServerMetricsRecorder) {
						handler = new MicrometerHttpServerMetricsHandler((MicrometerHttpServerMetricsRecorder) metricsRecorder, uriTagValue);
					}
					else if (metricsRecorder instanceof ContextAwareHttpServerMetricsRecorder) {
						handler = new ContextAwareHttpServerMetricsHandler((ContextAwareHttpServerMetricsRecorder) metricsRecorder, uriTagValue);
					}
					else {
						handler = new HttpServerMetricsHandler((HttpServerMetricsRecorder) metricsRecorder, uriTagValue);
					}
				}
				pipeline.addBefore(NettyPipeline.ReactiveBridge, NettyPipeline.HttpMetricsHandler, handler);
			}
		}

		if (log.isDebugEnabled()) {
			log.debug(format(ch, "Initialized HTTP/2 stream pipeline {}"), pipeline);
		}
	}

	@Nullable
	static BiPredicate<HttpServerRequest, HttpServerResponse> compressPredicate(
			@Nullable BiPredicate<HttpServerRequest, HttpServerResponse> compressionPredicate,
			int minResponseSize) {

		if (minResponseSize <= 0) {
			return compressionPredicate;
		}

		BiPredicate<HttpServerRequest, HttpServerResponse> lengthPredicate =
				(req, res) -> {
					String length = res.responseHeaders()
					                   .get(HttpHeaderNames.CONTENT_LENGTH);

					if (length == null) {
						return true;
					}

					try {
						return Long.parseLong(length) >= minResponseSize;
					}
					catch (NumberFormatException nfe) {
						return true;
					}
				};

		if (compressionPredicate != null) {
			lengthPredicate = lengthPredicate.and(compressionPredicate);
		}
		return lengthPredicate;
	}

	static void configureH2Pipeline(ChannelPipeline p,
			boolean accessLogEnabled,
			@Nullable Function<AccessLogArgProvider, AccessLog> accessLog,
			@Nullable BiPredicate<HttpServerRequest, HttpServerResponse> compressPredicate,
			ServerCookieDecoder cookieDecoder,
			ServerCookieEncoder cookieEncoder,
			HttpServerFormDecoderProvider formDecoderProvider,
			@Nullable BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler,
			Http2Settings http2Settings,
			HttpMessageLogFactory httpMessageLogFactory,
			@Nullable Duration idleTimeout,
			ConnectionObserver listener,
			@Nullable BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>> mapHandle,
			@Nullable ChannelMetricsRecorder metricsRecorder,
			int minCompressionSize,
			ChannelOperations.OnSetup opsFactory,
			@Nullable Function<String, String> uriTagValue,
			boolean validate) {
		p.remove(NettyPipeline.ReactiveBridge);

		Http2FrameCodecBuilder http2FrameCodecBuilder =
				Http2FrameCodecBuilder.forServer()
				                      .validateHeaders(validate)
				                      .initialSettings(http2Settings);

		if (p.get(NettyPipeline.LoggingHandler) != null) {
			http2FrameCodecBuilder.frameLogger(new Http2FrameLogger(LogLevel.DEBUG,
					"reactor.netty.http.server.h2"));
		}

		p.addLast(NettyPipeline.HttpCodec, http2FrameCodecBuilder.build())
		 .addLast(NettyPipeline.H2MultiplexHandler,
		          new Http2MultiplexHandler(new H2Codec(accessLogEnabled, accessLog, compressPredicate, cookieDecoder,
		                  cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener,
		                  mapHandle, metricsRecorder, minCompressionSize, opsFactory, uriTagValue)));

		IdleTimeoutHandler.addIdleTimeoutHandler(p, idleTimeout);

		if (metricsRecorder != null) {
			if (metricsRecorder instanceof MicrometerHttpServerMetricsRecorder) {
				// For sake of performance, we can replace the ChannelMetricsHandler because the MicrometerHttpServerMetricsRecorder
				// is interested only in connections metrics .
				p.replace(NettyPipeline.ChannelMetricsHandler, NettyPipeline.ChannelMetricsHandler,
						new H2ChannelMetricsHandler(metricsRecorder));
			}
		}
	}

	@SuppressWarnings("deprecation")
	static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p,
			boolean accessLogEnabled,
			@Nullable Function<AccessLogArgProvider, AccessLog> accessLog,
			@Nullable BiPredicate<HttpServerRequest, HttpServerResponse> compressPredicate,
			ServerCookieDecoder cookieDecoder,
			ServerCookieEncoder cookieEncoder,
			HttpRequestDecoderSpec decoder,
			HttpServerFormDecoderProvider formDecoderProvider,
			@Nullable BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler,
			Http2Settings http2Settings,
			HttpMessageLogFactory httpMessageLogFactory,
			@Nullable Duration idleTimeout,
			ConnectionObserver listener,
			@Nullable BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>> mapHandle,
			int maxKeepAliveRequests,
			@Nullable ChannelMetricsRecorder metricsRecorder,
			int minCompressionSize,
			ChannelOperations.OnSetup opsFactory,
			@Nullable Function<String, String> uriTagValue) {
		HttpServerCodec httpServerCodec =
				new HttpServerCodec(decoder.maxInitialLineLength(), decoder.maxHeaderSize(),
						decoder.maxChunkSize(), decoder.validateHeaders(), decoder.initialBufferSize(),
						decoder.allowDuplicateContentLengths());

		Http11OrH2CleartextCodec upgrader = new Http11OrH2CleartextCodec(accessLogEnabled, accessLog, compressPredicate,
				cookieDecoder, cookieEncoder, p.get(NettyPipeline.LoggingHandler) != null, formDecoderProvider,
				forwardedHeaderHandler, http2Settings, httpMessageLogFactory, listener, mapHandle, metricsRecorder,
				minCompressionSize, opsFactory, uriTagValue, decoder.validateHeaders());

		ChannelHandler http2ServerHandler = new H2CleartextCodec(upgrader);
		CleartextHttp2ServerUpgradeHandler h2cUpgradeHandler = new CleartextHttp2ServerUpgradeHandler(
				httpServerCodec,
				new HttpServerUpgradeHandler(httpServerCodec, upgrader, decoder.h2cMaxContentLength()),
				http2ServerHandler);

		p.addBefore(NettyPipeline.ReactiveBridge,
		            NettyPipeline.H2CUpgradeHandler, h2cUpgradeHandler)
		 .addBefore(NettyPipeline.ReactiveBridge,
		            NettyPipeline.HttpTrafficHandler,
		            new HttpTrafficHandler(compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider,
		                    forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, mapHandle, maxKeepAliveRequests));

		if (accessLogEnabled) {
			p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.AccessLogHandler, AccessLogHandlerFactory.H1.create(accessLog));
		}

		boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0;

		if (alwaysCompress) {
			p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, new SimpleCompressionHandler());
		}

		if (metricsRecorder != null) {
			if (metricsRecorder instanceof HttpServerMetricsRecorder) {
				ChannelHandler handler;
				if (metricsRecorder instanceof MicrometerHttpServerMetricsRecorder) {
					handler = new MicrometerHttpServerMetricsHandler((MicrometerHttpServerMetricsRecorder) metricsRecorder, uriTagValue);
				}
				else if (metricsRecorder instanceof ContextAwareHttpServerMetricsRecorder) {
					handler = new ContextAwareHttpServerMetricsHandler((ContextAwareHttpServerMetricsRecorder) metricsRecorder, uriTagValue);
				}
				else {
					handler = new HttpServerMetricsHandler((HttpServerMetricsRecorder) metricsRecorder, uriTagValue);
				}
				p.addAfter(NettyPipeline.HttpTrafficHandler, NettyPipeline.HttpMetricsHandler, handler);
				if (metricsRecorder instanceof MicrometerHttpServerMetricsRecorder) {
					// For sake of performance, we can remove the ChannelMetricsHandler because the MicrometerHttpServerMetricsRecorder
					// does not implement metrics on TCP protocol level.
					p.remove(NettyPipeline.ChannelMetricsHandler);
				}
			}
		}
	}

	@SuppressWarnings("deprecation")
	static void configureHttp11Pipeline(ChannelPipeline p,
			boolean accessLogEnabled,
			@Nullable Function<AccessLogArgProvider, AccessLog> accessLog,
			@Nullable BiPredicate<HttpServerRequest, HttpServerResponse> compressPredicate,
			ServerCookieDecoder cookieDecoder,
			ServerCookieEncoder cookieEncoder,
			HttpRequestDecoderSpec decoder,
			HttpServerFormDecoderProvider formDecoderProvider,
			@Nullable BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler,
			HttpMessageLogFactory httpMessageLogFactory,
			@Nullable Duration idleTimeout,
			ConnectionObserver listener,
			@Nullable BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>> mapHandle,
			int maxKeepAliveRequests,
			@Nullable ChannelMetricsRecorder metricsRecorder,
			int minCompressionSize,
			@Nullable Function<String, String> uriTagValue) {
		p.addBefore(NettyPipeline.ReactiveBridge,
		            NettyPipeline.HttpCodec,
		            new HttpServerCodec(decoder.maxInitialLineLength(), decoder.maxHeaderSize(),
		                    decoder.maxChunkSize(), decoder.validateHeaders(), decoder.initialBufferSize(),
		                    decoder.allowDuplicateContentLengths()))
		 .addBefore(NettyPipeline.ReactiveBridge,
		            NettyPipeline.HttpTrafficHandler,
		            new HttpTrafficHandler(compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider,
		                    forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, mapHandle, maxKeepAliveRequests));

		if (accessLogEnabled) {
			p.addAfter(NettyPipeline.HttpCodec, NettyPipeline.AccessLogHandler, AccessLogHandlerFactory.H1.create(accessLog));
		}

		boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0;

		if (alwaysCompress) {
			p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, new SimpleCompressionHandler());
		}

		if (metricsRecorder != null) {
			if (metricsRecorder instanceof HttpServerMetricsRecorder) {
				ChannelHandler handler;
				if (metricsRecorder instanceof MicrometerHttpServerMetricsRecorder) {
					handler = new MicrometerHttpServerMetricsHandler((MicrometerHttpServerMetricsRecorder) metricsRecorder, uriTagValue);
				}
				else if (metricsRecorder instanceof ContextAwareHttpServerMetricsRecorder) {
					handler = new ContextAwareHttpServerMetricsHandler((ContextAwareHttpServerMetricsRecorder) metricsRecorder, uriTagValue);
				}
				else {
					handler = new HttpServerMetricsHandler((HttpServerMetricsRecorder) metricsRecorder, uriTagValue);
				}
				p.addAfter(NettyPipeline.HttpTrafficHandler, NettyPipeline.HttpMetricsHandler, handler);
				if (metricsRecorder instanceof MicrometerHttpServerMetricsRecorder) {
					// For sake of performance, we can remove the ChannelMetricsHandler because the MicrometerHttpServerMetricsRecorder
					// does not implement metrics on TCP protocol level.
					p.remove(NettyPipeline.ChannelMetricsHandler);
				}
			}
		}
	}

	static final boolean ACCESS_LOG = Boolean.parseBoolean(System.getProperty(ACCESS_LOG_ENABLED, "false"));

	static final int h2 = 0b010;

	static final int h2c = 0b001;

	static final int h11 = 0b100;

	static final int h11orH2 = h11 | h2;

	static final int h11orH2C = h11 | h2c;

	static final Http2StreamFrameToHttpObjectCodec HTTP2_STREAM_FRAME_TO_HTTP_OBJECT =
			new Http2StreamFrameToHttpObjectCodec(true);

	static final Logger log = Loggers.getLogger(HttpServerConfig.class);

	static final LoggingHandler LOGGING_HANDLER =
			AdvancedByteBufFormat.HEX_DUMP
					.toLoggingHandler(HttpServer.class.getName(), LogLevel.DEBUG, Charset.defaultCharset());

	/**
	 * Default value whether the SSL debugging on the server side will be enabled/disabled,
	 * fallback to SSL debugging disabled
	 */
	static final boolean SSL_DEBUG = Boolean.parseBoolean(System.getProperty(ReactorNetty.SSL_SERVER_DEBUG, "false"));

	static final class H2ChannelMetricsHandler extends AbstractChannelMetricsHandler {

		final ChannelMetricsRecorder recorder;

		H2ChannelMetricsHandler(ChannelMetricsRecorder recorder) {
			super(null, true);
			this.recorder = recorder;
		}

		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg) {
			ctx.fireChannelRead(msg);
		}

		@Override
		public void channelRegistered(ChannelHandlerContext ctx) {
			ctx.fireChannelRegistered();
		}

		@Override
		public ChannelHandler connectMetricsHandler() {
			return null;
		}

		@Override
		public ChannelHandler tlsMetricsHandler() {
			return null;
		}

		@Override
		public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
			ctx.fireExceptionCaught(cause);
		}

		@Override
		public ChannelMetricsRecorder recorder() {
			return recorder;
		}

		@Override
		@SuppressWarnings("FutureReturnValueIgnored")
		public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
			//"FutureReturnValueIgnored" this is deliberate
			ctx.write(msg, promise);
		}
	}

	static final class H2CleartextCodec extends ChannelHandlerAdapter {

		final Http11OrH2CleartextCodec upgrader;
		final boolean addHttp2FrameCodec;
		final boolean removeMetricsHandler;

		/**
		 * Used when full H2 preface is received
		 */
		H2CleartextCodec(Http11OrH2CleartextCodec upgrader) {
			this(upgrader, true, true);
		}

		/**
		 * Used when upgrading from HTTP/1.1 to H2. When an upgrade happens {@link Http2FrameCodec}
		 * is added by {@link Http2ServerUpgradeCodec}
		 */
		H2CleartextCodec(Http11OrH2CleartextCodec upgrader, boolean addHttp2FrameCodec, boolean removeMetricsHandler) {
			this.upgrader = upgrader;
			this.addHttp2FrameCodec = addHttp2FrameCodec;
			this.removeMetricsHandler = removeMetricsHandler;
		}

		@Override
		public void handlerAdded(ChannelHandlerContext ctx) {
			ChannelPipeline pipeline = ctx.pipeline();
			if (addHttp2FrameCodec) {
				pipeline.addAfter(ctx.name(), NettyPipeline.HttpCodec, upgrader.http2FrameCodec);
			}

			pipeline.addAfter(ctx.pipeline().context(upgrader.http2FrameCodec).name(),
					NettyPipeline.H2MultiplexHandler, new Http2MultiplexHandler(upgrader));

			pipeline.remove(this);

			if (pipeline.get(NettyPipeline.AccessLogHandler) != null) {
				pipeline.remove(NettyPipeline.AccessLogHandler);
			}
			if (pipeline.get(NettyPipeline.CompressionHandler) != null) {
				pipeline.remove(NettyPipeline.CompressionHandler);
			}
			if (removeMetricsHandler && pipeline.get(NettyPipeline.HttpMetricsHandler) != null) {
				AbstractHttpServerMetricsHandler handler =
						(AbstractHttpServerMetricsHandler) pipeline.get(NettyPipeline.HttpMetricsHandler);
				if (handler.recorder() instanceof MicrometerHttpServerMetricsRecorder) {
					pipeline.replace(NettyPipeline.HttpMetricsHandler, NettyPipeline.ChannelMetricsHandler,
							new H2ChannelMetricsHandler(handler.recorder()));
				}
				else {
					pipeline.remove(NettyPipeline.HttpMetricsHandler);
				}
			}
			pipeline.remove(NettyPipeline.HttpTrafficHandler);
			pipeline.remove(NettyPipeline.ReactiveBridge);
		}
	}

	static final class H2Codec extends ChannelInitializer<Channel> {

		final boolean                                                 accessLogEnabled;
		final Function<AccessLogArgProvider, AccessLog>               accessLog;
		final BiPredicate<HttpServerRequest, HttpServerResponse>      compressPredicate;
		final ServerCookieDecoder                                     cookieDecoder;
		final ServerCookieEncoder                                     cookieEncoder;
		final HttpServerFormDecoderProvider                           formDecoderProvider;
		final BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler;
		final HttpMessageLogFactory                                   httpMessageLogFactory;
		final ConnectionObserver                                      listener;
		final BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>>
		                                                              mapHandle;
		final ChannelMetricsRecorder                                  metricsRecorder;
		final int                                                     minCompressionSize;
		final ChannelOperations.OnSetup                               opsFactory;
		final Function<String, String>                                uriTagValue;

		H2Codec(
				boolean accessLogEnabled,
				@Nullable Function<AccessLogArgProvider, AccessLog> accessLog,
				@Nullable BiPredicate<HttpServerRequest, HttpServerResponse> compressPredicate,
				ServerCookieDecoder decoder,
				ServerCookieEncoder encoder,
				HttpServerFormDecoderProvider formDecoderProvider,
				@Nullable BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler,
				HttpMessageLogFactory httpMessageLogFactory,
				ConnectionObserver listener,
				@Nullable BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>> mapHandle,
				@Nullable ChannelMetricsRecorder metricsRecorder,
				int minCompressionSize,
				ChannelOperations.OnSetup opsFactory,
				@Nullable Function<String, String> uriTagValue) {
			this.accessLogEnabled = accessLogEnabled;
			this.accessLog = accessLog;
			this.compressPredicate = compressPredicate;
			this.cookieDecoder = decoder;
			this.cookieEncoder = encoder;
			this.formDecoderProvider = formDecoderProvider;
			this.forwardedHeaderHandler = forwardedHeaderHandler;
			this.httpMessageLogFactory = httpMessageLogFactory;
			this.listener = listener;
			this.mapHandle = mapHandle;
			this.metricsRecorder = metricsRecorder;
			this.minCompressionSize = minCompressionSize;
			this.opsFactory = opsFactory;
			this.uriTagValue = uriTagValue;
		}

		@Override
		protected void initChannel(Channel ch) {
			ch.pipeline().remove(this);
			addStreamHandlers(ch, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder,
					formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, metricsRecorder,
					minCompressionSize, opsFactory, uriTagValue);
		}
	}

	static final class Http11OrH2CleartextCodec extends ChannelInitializer<Channel>
			implements HttpServerUpgradeHandler.UpgradeCodecFactory {

		final boolean                                                 accessLogEnabled;
		final Function<AccessLogArgProvider, AccessLog>               accessLog;
		final BiPredicate<HttpServerRequest, HttpServerResponse>      compressPredicate;
		final ServerCookieDecoder                                     cookieDecoder;
		final ServerCookieEncoder                                     cookieEncoder;
		final HttpServerFormDecoderProvider                           formDecoderProvider;
		final BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler;
		final Http2FrameCodec                                         http2FrameCodec;
		final HttpMessageLogFactory                                   httpMessageLogFactory;
		final ConnectionObserver                                      listener;
		final BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>>
		                                                              mapHandle;
		final ChannelMetricsRecorder                                  metricsRecorder;
		final int                                                     minCompressionSize;
		final ChannelOperations.OnSetup                               opsFactory;
		final Function<String, String>                                uriTagValue;

		Http11OrH2CleartextCodec(
				boolean accessLogEnabled,
				@Nullable Function<AccessLogArgProvider, AccessLog> accessLog,
				@Nullable BiPredicate<HttpServerRequest, HttpServerResponse> compressPredicate,
				ServerCookieDecoder cookieDecoder,
				ServerCookieEncoder cookieEncoder,
				boolean debug,
				HttpServerFormDecoderProvider formDecoderProvider,
				@Nullable BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler,
				Http2Settings http2Settings,
				HttpMessageLogFactory httpMessageLogFactory,
				ConnectionObserver listener,
				@Nullable BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>> mapHandle,
				@Nullable ChannelMetricsRecorder metricsRecorder,
				int minCompressionSize,
				ChannelOperations.OnSetup opsFactory,
				@Nullable Function<String, String> uriTagValue,
				boolean validate) {
			this.accessLogEnabled = accessLogEnabled;
			this.accessLog = accessLog;
			this.compressPredicate = compressPredicate;
			this.cookieDecoder = cookieDecoder;
			this.cookieEncoder = cookieEncoder;
			this.formDecoderProvider = formDecoderProvider;
			this.forwardedHeaderHandler = forwardedHeaderHandler;
			Http2FrameCodecBuilder http2FrameCodecBuilder =
					Http2FrameCodecBuilder.forServer()
					                      .validateHeaders(validate)
					                      .initialSettings(http2Settings);

			if (debug) {
				http2FrameCodecBuilder.frameLogger(new Http2FrameLogger(
						LogLevel.DEBUG,
						"reactor.netty.http.server.h2"));
			}
			this.http2FrameCodec = http2FrameCodecBuilder.build();
			this.httpMessageLogFactory = httpMessageLogFactory;
			this.listener = listener;
			this.mapHandle = mapHandle;
			this.metricsRecorder = metricsRecorder;
			this.minCompressionSize = minCompressionSize;
			this.opsFactory = opsFactory;
			this.uriTagValue = uriTagValue;
		}

		/**
		 * Inline channel initializer
		 */
		@Override
		protected void initChannel(Channel ch) {
			ch.pipeline().remove(this);
			addStreamHandlers(ch, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder,
					formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, metricsRecorder,
					minCompressionSize, opsFactory, uriTagValue);
		}

		@Override
		@Nullable
		public HttpServerUpgradeHandler.UpgradeCodec newUpgradeCodec(CharSequence protocol) {
			if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) {
				return new Http2ServerUpgradeCodec(http2FrameCodec, new H2CleartextCodec(this, false, false));
			}
			else {
				return null;
			}
		}
	}

	static final class H2OrHttp11Codec extends ApplicationProtocolNegotiationHandler {

		final boolean                                                 accessLogEnabled;
		final Function<AccessLogArgProvider, AccessLog>               accessLog;
		final BiPredicate<HttpServerRequest, HttpServerResponse>      compressPredicate;
		final ServerCookieDecoder                                     cookieDecoder;
		final ServerCookieEncoder                                     cookieEncoder;
		final HttpRequestDecoderSpec                                  decoder;
		final HttpServerFormDecoderProvider                           formDecoderProvider;
		final BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler;
		final Http2Settings                                           http2Settings;
		final HttpMessageLogFactory                                   httpMessageLogFactory;
		final Duration                                                idleTimeout;
		final ConnectionObserver                                      listener;
		final BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>>
		                                                              mapHandle;
		final int                                                     maxKeepAliveRequests;
		final ChannelMetricsRecorder                                  metricsRecorder;
		final int                                                     minCompressionSize;
		final ChannelOperations.OnSetup                               opsFactory;
		final Function<String, String>                                uriTagValue;

		H2OrHttp11Codec(HttpServerChannelInitializer initializer, ConnectionObserver listener) {
			super(ApplicationProtocolNames.HTTP_1_1);
			this.accessLogEnabled = initializer.accessLogEnabled;
			this.accessLog = initializer.accessLog;
			this.compressPredicate = compressPredicate(initializer.compressPredicate, initializer.minCompressionSize);
			this.cookieDecoder = initializer.cookieDecoder;
			this.cookieEncoder = initializer.cookieEncoder;
			this.decoder = initializer.decoder;
			this.formDecoderProvider = initializer.formDecoderProvider;
			this.forwardedHeaderHandler = initializer.forwardedHeaderHandler;
			this.http2Settings = initializer.http2Settings;
			this.httpMessageLogFactory = initializer.httpMessageLogFactory;
			this.idleTimeout = initializer.idleTimeout;
			this.listener = listener;
			this.mapHandle = initializer.mapHandle;
			this.maxKeepAliveRequests = initializer.maxKeepAliveRequests;
			this.metricsRecorder = initializer.metricsRecorder;
			this.minCompressionSize = initializer.minCompressionSize;
			this.opsFactory = initializer.opsFactory;
			this.uriTagValue = initializer.uriTagValue;
		}

		@Override
		protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
			if (log.isDebugEnabled()) {
				log.debug(format(ctx.channel(), "Negotiated application-level protocol [" + protocol + "]"));
			}

			ChannelPipeline p = ctx.pipeline();

			if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
				configureH2Pipeline(p, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder,
						formDecoderProvider, forwardedHeaderHandler, http2Settings, httpMessageLogFactory, idleTimeout,
						listener, mapHandle, metricsRecorder, minCompressionSize, opsFactory, uriTagValue, decoder.validateHeaders());
				return;
			}

			if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
				configureHttp11Pipeline(p, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder,
						decoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener,
						mapHandle, maxKeepAliveRequests, metricsRecorder, minCompressionSize, uriTagValue);
				return;
			}

			throw new IllegalStateException("unknown protocol: " + protocol);
		}
	}

	static final class HttpServerChannelInitializer implements ChannelPipelineConfigurer {

		final boolean                                                 accessLogEnabled;
		final Function<AccessLogArgProvider, AccessLog>               accessLog;
		final BiPredicate<HttpServerRequest, HttpServerResponse>      compressPredicate;
		final ServerCookieDecoder                                     cookieDecoder;
		final ServerCookieEncoder                                     cookieEncoder;
		final HttpRequestDecoderSpec                                  decoder;
		final HttpServerFormDecoderProvider                           formDecoderProvider;
		final BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> forwardedHeaderHandler;
		final Http2Settings                                           http2Settings;
		final HttpMessageLogFactory                                   httpMessageLogFactory;
		final Duration                                                idleTimeout;
		final BiFunction<? super Mono<Void>, ? super Connection, ? extends Mono<Void>>
		                                                              mapHandle;
		final int                                                     maxKeepAliveRequests;
		final ChannelMetricsRecorder                                  metricsRecorder;
		final int                                                     minCompressionSize;
		final ChannelOperations.OnSetup                               opsFactory;
		final int                                                     protocols;
		final ProxyProtocolSupportType                                proxyProtocolSupportType;
		final boolean                                                 redirectHttpToHttps;
		final SslProvider                                             sslProvider;
		final Function<String, String>                                uriTagValue;

		HttpServerChannelInitializer(HttpServerConfig config) {
			this.accessLogEnabled = config.accessLogEnabled;
			this.accessLog = config.accessLog;
			this.compressPredicate = config.compressPredicate;
			this.cookieDecoder = config.cookieDecoder;
			this.cookieEncoder = config.cookieEncoder;
			this.decoder = config.decoder;
			this.formDecoderProvider = config.formDecoderProvider;
			this.forwardedHeaderHandler = config.forwardedHeaderHandler;
			this.http2Settings = config.http2Settings();
			this.httpMessageLogFactory = config.httpMessageLogFactory;
			this.idleTimeout = config.idleTimeout;
			this.mapHandle = config.mapHandle;
			this.maxKeepAliveRequests = config.maxKeepAliveRequests;
			this.metricsRecorder = config.metricsRecorderInternal();
			this.minCompressionSize = config.minCompressionSize;
			this.opsFactory = config.channelOperationsProvider();
			this.protocols = config._protocols;
			this.proxyProtocolSupportType = config.proxyProtocolSupportType;
			this.redirectHttpToHttps = config.redirectHttpToHttps;
			this.sslProvider = config.sslProvider;
			this.uriTagValue = config.uriTagValue;
		}

		@Override
		public void onChannelInit(ConnectionObserver observer, Channel channel, @Nullable SocketAddress remoteAddress) {
			boolean needRead = false;

			if (sslProvider != null) {
				ChannelPipeline pipeline = channel.pipeline();
				if (redirectHttpToHttps && (protocols & h2) != h2) {
					NonSslRedirectDetector nonSslRedirectDetector = new NonSslRedirectDetector(sslProvider,
							remoteAddress,
							SSL_DEBUG);
					pipeline.addFirst(NettyPipeline.NonSslRedirectDetector, nonSslRedirectDetector);
				}
				else {
					sslProvider.addSslHandler(channel, remoteAddress, SSL_DEBUG);
				}

				if ((protocols & h11orH2) == h11orH2) {
					channel.pipeline()
					       .addBefore(NettyPipeline.ReactiveBridge,
					                  NettyPipeline.H2OrHttp11Codec,
					                  new H2OrHttp11Codec(this, observer));
				}
				else if ((protocols & h11) == h11) {
					configureHttp11Pipeline(
							channel.pipeline(),
							accessLogEnabled,
							accessLog,
							compressPredicate(compressPredicate, minCompressionSize),
							cookieDecoder,
							cookieEncoder,
							decoder,
							formDecoderProvider,
							forwardedHeaderHandler,
							httpMessageLogFactory,
							idleTimeout,
							observer,
							mapHandle,
							maxKeepAliveRequests,
							metricsRecorder,
							minCompressionSize,
							uriTagValue);
				}
				else if ((protocols & h2) == h2) {
					configureH2Pipeline(
							channel.pipeline(),
							accessLogEnabled,
							accessLog,
							compressPredicate(compressPredicate, minCompressionSize),
							cookieDecoder,
							cookieEncoder,
							formDecoderProvider,
							forwardedHeaderHandler,
							http2Settings,
							httpMessageLogFactory,
							idleTimeout,
							observer,
							mapHandle,
							metricsRecorder,
							minCompressionSize,
							opsFactory,
							uriTagValue,
							decoder.validateHeaders());
				}
			}
			else {
				if ((protocols & h11orH2C) == h11orH2C) {
					configureHttp11OrH2CleartextPipeline(
							channel.pipeline(),
							accessLogEnabled,
							accessLog,
							compressPredicate(compressPredicate, minCompressionSize),
							cookieDecoder,
							cookieEncoder,
							decoder,
							formDecoderProvider,
							forwardedHeaderHandler,
							http2Settings,
							httpMessageLogFactory,
							idleTimeout,
							observer,
							mapHandle,
							maxKeepAliveRequests,
							metricsRecorder,
							minCompressionSize,
							opsFactory,
							uriTagValue);
				}
				else if ((protocols & h11) == h11) {
					configureHttp11Pipeline(
							channel.pipeline(),
							accessLogEnabled,
							accessLog,
							compressPredicate(compressPredicate, minCompressionSize),
							cookieDecoder,
							cookieEncoder,
							decoder,
							formDecoderProvider,
							forwardedHeaderHandler,
							httpMessageLogFactory,
							idleTimeout,
							observer,
							mapHandle,
							maxKeepAliveRequests,
							metricsRecorder,
							minCompressionSize,
							uriTagValue);
				}
				else if ((protocols & h2c) == h2c) {
					configureH2Pipeline(
							channel.pipeline(),
							accessLogEnabled,
							accessLog,
							compressPredicate(compressPredicate, minCompressionSize),
							cookieDecoder,
							cookieEncoder,
							formDecoderProvider,
							forwardedHeaderHandler,
							http2Settings,
							httpMessageLogFactory,
							idleTimeout,
							observer,
							mapHandle,
							metricsRecorder,
							minCompressionSize,
							opsFactory,
							uriTagValue,
							decoder.validateHeaders());
					needRead = true;
				}
			}

			if (proxyProtocolSupportType == ProxyProtocolSupportType.ON) {
				channel.pipeline()
				       .addFirst(NettyPipeline.ProxyProtocolDecoder, new HAProxyMessageDecoder())
				       .addAfter(NettyPipeline.ProxyProtocolDecoder,
				                 NettyPipeline.ProxyProtocolReader,
				                 new HAProxyMessageReader());
			}
			else if (proxyProtocolSupportType == ProxyProtocolSupportType.AUTO) {
				channel.pipeline()
				       .addFirst(NettyPipeline.ProxyProtocolDecoder, new HAProxyMessageDetector());
			}

			if (needRead) {
				channel.read();
			}
		}
	}
}