<commit_before>import { resolve, join, sep } from 'path'
import { parse as parseUrl } from 'url'
import { parse as parseQs } from 'querystring'
import fs from 'fs'
import http, { STATUS_CODES } from 'http'
import {
  renderToHTML,
  renderErrorToHTML,
  sendHTML,
  serveStatic,
  renderScript,
  renderScriptError
} from './render'
import Router from './router'
import HotReloader from './hot-reloader'
import { resolveFromList } from './resolve'
import getConfig from './config'
// We need to go up one more level since we are in the `dist` directory
import pkg from '../../package'

const internalPrefixes = [
  /^\/_next\//,
  /^\/static\//
]

export default class Server {
  constructor ({ dir = '.', dev = false, staticMarkup = false, quiet = false, conf = null } = {}) {
    this.dir = resolve(dir)
    this.dev = dev
    this.quiet = quiet
    this.router = new Router()
    this.hotReloader = dev ? new HotReloader(this.dir, { quiet, conf }) : null
    this.http = null
    this.config = getConfig(this.dir, conf)
    this.dist = this.config.distDir
    this.buildStats = !dev ? require(join(this.dir, this.dist, 'build-stats.json')) : null
    this.buildId = !dev ? this.readBuildId() : '-'
    this.renderOpts = {
      dev,
      staticMarkup,
      dir: this.dir,
      hotReloader: this.hotReloader,
      buildStats: this.buildStats,
      buildId: this.buildId,
      assetPrefix: this.config.assetPrefix.replace(/\/$/, '')
    }

    this.defineRoutes()
  }

  handleRequest (req, res, parsedUrl) {
    // Parse url if parsedUrl not provided
    if (!parsedUrl) {
      parsedUrl = parseUrl(req.url, true)
    }

    // Parse the querystring ourselves if the user doesn't handle querystring parsing
    if (typeof parsedUrl.query === 'string') {
      parsedUrl.query = parseQs(parsedUrl.query)
    }

    return this.run(req, res, parsedUrl)
    .catch((err) => {
      if (!this.quiet) console.error(err)
      res.statusCode = 500
      res.end(STATUS_CODES[500])
    })
  }

  getRequestHandler () {
    return this.handleRequest.bind(this)
  }

  async prepare () {
    if (this.hotReloader) {
      await this.hotReloader.start()
    }
  }

  async close () {
    if (this.hotReloader) {
      await this.hotReloader.stop()
    }

    if (this.http) {
      await new Promise((resolve, reject) => {
        this.http.close((err) => {
          if (err) return reject(err)
          return resolve()
        })
      })
    }
  }

  defineRoutes () {
    const routes = {
      '/_next-prefetcher.js': async (req, res, params) => {
        const p = join(__dirname, '../client/next-prefetcher-bundle.js')
        await this.serveStatic(req, res, p)
      },

      '/_next/:hash/manifest.js': async (req, res, params) => {
        if (!this.dev) return this.send404(res)

        this.handleBuildHash('manifest.js', params.hash, res)
        const p = join(this.dir, `${this.dist}/manifest.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:hash/main.js': async (req, res, params) => {
        if (!this.dev) return this.send404(res)

        this.handleBuildHash('main.js', params.hash, res)
        const p = join(this.dir, `${this.dist}/main.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:hash/commons.js': async (req, res, params) => {
        if (!this.dev) return this.send404(res)

        this.handleBuildHash('commons.js', params.hash, res)
        const p = join(this.dir, `${this.dist}/commons.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:hash/app.js': async (req, res, params) => {
        if (this.dev) return this.send404(res)

        this.handleBuildHash('app.js', params.hash, res)
        const p = join(this.dir, `${this.dist}/app.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:buildId/page/_error': async (req, res, params) => {
        if (!this.handleBuildId(params.buildId, res)) {
          const error = new Error('INVALID_BUILD_ID')
          const customFields = { buildIdMismatched: true }

          return await renderScriptError(req, res, '/_error', error, customFields, this.renderOpts)
        }

        const p = join(this.dir, `${this.dist}/bundles/pages/_error.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:buildId/page/:path*': async (req, res, params) => {
        const paths = params.path || ['']
        const page = `/${paths.join('/')}`

        if (!this.handleBuildId(params.buildId, res)) {
          const error = new Error('INVALID_BUILD_ID')
          const customFields = { buildIdMismatched: true }

          return await renderScriptError(req, res, page, error, customFields, this.renderOpts)
        }

        if (this.dev) {
          try {
            await this.hotReloader.ensurePage(page)
          } catch (error) {
            return await renderScriptError(req, res, page, error, {}, this.renderOpts)
          }

          const compilationErr = await this.getCompilationError(page, req, res)
          if (compilationErr) {
            const customFields = { statusCode: 500 }
            return await renderScriptError(req, res, page, compilationErr, customFields, this.renderOpts)
          }
        }

        await renderScript(req, res, page, this.renderOpts)
      },

      '/_next/:path+': async (req, res, params) => {
        const p = join(__dirname, '..', 'client', ...(params.path || []))
        await this.serveStatic(req, res, p)
      },

      '/static/:path+': async (req, res, params) => {
        const p = join(this.dir, 'static', ...(params.path || []))
        await this.serveStatic(req, res, p)
      }
    }

    if (this.config.useFileSystemPublicRoutes) {
      routes['/:path*'] = async (req, res, params, parsedUrl) => {
        const { pathname, query } = parsedUrl
        await this.render(req, res, pathname, query)<commit_msg>Make sure /static/ and /_next/ are valid internal urls. (#2631)<commit_after>import { resolve, join, sep } from 'path'
import { parse as parseUrl } from 'url'
import { parse as parseQs } from 'querystring'
import fs from 'fs'
import http, { STATUS_CODES } from 'http'
import {
  renderToHTML,
  renderErrorToHTML,
  sendHTML,
  serveStatic,
  renderScript,
  renderScriptError
} from './render'
import Router from './router'
import HotReloader from './hot-reloader'
import { resolveFromList } from './resolve'
import getConfig from './config'
// We need to go up one more level since we are in the `dist` directory
import pkg from '../../package'

const internalPrefixes = [
  /^\/_next\//,
  /^\/static\//
]

export default class Server {
  constructor ({ dir = '.', dev = false, staticMarkup = false, quiet = false, conf = null } = {}) {
    this.dir = resolve(dir)
    this.dev = dev
    this.quiet = quiet
    this.router = new Router()
    this.hotReloader = dev ? new HotReloader(this.dir, { quiet, conf }) : null
    this.http = null
    this.config = getConfig(this.dir, conf)
    this.dist = this.config.distDir
    this.buildStats = !dev ? require(join(this.dir, this.dist, 'build-stats.json')) : null
    this.buildId = !dev ? this.readBuildId() : '-'
    this.renderOpts = {
      dev,
      staticMarkup,
      dir: this.dir,
      hotReloader: this.hotReloader,
      buildStats: this.buildStats,
      buildId: this.buildId,
      assetPrefix: this.config.assetPrefix.replace(/\/$/, '')
    }

    this.defineRoutes()
  }

  handleRequest (req, res, parsedUrl) {
    // Parse url if parsedUrl not provided
    if (!parsedUrl) {
      parsedUrl = parseUrl(req.url, true)
    }

    // Parse the querystring ourselves if the user doesn't handle querystring parsing
    if (typeof parsedUrl.query === 'string') {
      parsedUrl.query = parseQs(parsedUrl.query)
    }

    return this.run(req, res, parsedUrl)
    .catch((err) => {
      if (!this.quiet) console.error(err)
      res.statusCode = 500
      res.end(STATUS_CODES[500])
    })
  }

  getRequestHandler () {
    return this.handleRequest.bind(this)
  }

  async prepare () {
    if (this.hotReloader) {
      await this.hotReloader.start()
    }
  }

  async close () {
    if (this.hotReloader) {
      await this.hotReloader.stop()
    }

    if (this.http) {
      await new Promise((resolve, reject) => {
        this.http.close((err) => {
          if (err) return reject(err)
          return resolve()
        })
      })
    }
  }

  defineRoutes () {
    const routes = {
      '/_next-prefetcher.js': async (req, res, params) => {
        const p = join(__dirname, '../client/next-prefetcher-bundle.js')
        await this.serveStatic(req, res, p)
      },

      '/_next/:hash/manifest.js': async (req, res, params) => {
        if (!this.dev) return this.send404(res)

        this.handleBuildHash('manifest.js', params.hash, res)
        const p = join(this.dir, `${this.dist}/manifest.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:hash/main.js': async (req, res, params) => {
        if (!this.dev) return this.send404(res)

        this.handleBuildHash('main.js', params.hash, res)
        const p = join(this.dir, `${this.dist}/main.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:hash/commons.js': async (req, res, params) => {
        if (!this.dev) return this.send404(res)

        this.handleBuildHash('commons.js', params.hash, res)
        const p = join(this.dir, `${this.dist}/commons.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:hash/app.js': async (req, res, params) => {
        if (this.dev) return this.send404(res)

        this.handleBuildHash('app.js', params.hash, res)
        const p = join(this.dir, `${this.dist}/app.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:buildId/page/_error': async (req, res, params) => {
        if (!this.handleBuildId(params.buildId, res)) {
          const error = new Error('INVALID_BUILD_ID')
          const customFields = { buildIdMismatched: true }

          return await renderScriptError(req, res, '/_error', error, customFields, this.renderOpts)
        }

        const p = join(this.dir, `${this.dist}/bundles/pages/_error.js`)
        await this.serveStatic(req, res, p)
      },

      '/_next/:buildId/page/:path*': async (req, res, params) => {
        const paths = params.path || ['']
        const page = `/${paths.join('/')}`

        if (!this.handleBuildId(params.buildId, res)) {
          const error = new Error('INVALID_BUILD_ID')
          const customFields = { buildIdMismatched: true }

          return await renderScriptError(req, res, page, error, customFields, this.renderOpts)
        }

        if (this.dev) {
          try {
            await this.hotReloader.ensurePage(page)
          } catch (error) {
            return await renderScriptError(req, res, page, error, {}, this.renderOpts)
          }

          const compilationErr = await this.getCompilationError(page, req, res)
          if (compilationErr) {
            const customFields = { statusCode: 500 }
            return await renderScriptError(req, res, page, compilationErr, customFields, this.renderOpts)
          }
        }

        await renderScript(req, res, page, this.renderOpts)
      },

      '/_next/:path?': async (req, res, params) => {
        const p = join(__dirname, '..', 'client', ...(params.path || []))
        await this.serveStatic(req, res, p)
      },

      '/static/:path?': async (req, res, params) => {
        const p = join(this.dir, 'static', ...(params.path || []))
        await this.serveStatic(req, res, p)
      }
    }

    if (this.config.useFileSystemPublicRoutes) {
      routes['/:path*'] = async (req, res, params, parsedUrl) => {
        const { pathname, query } = parsedUrl
        await this.render(req, res, pathname, query)
{'fraction_non_alphanumeric': 0.14839255499153978, 'fraction_numerical': 0.0038917089678511, 'mean_word_length': 2.425383946682121, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2681716', 'n_tokens_mistral': 3811, 'n_tokens_neox': 3785, 'n_words': 904}