<commit_before><?php
namespace MFS::AppServer::SCGI;

class Request
{
    private $headers = array();
    private $body = null;
    private $get = array();
    private $post = array();
    private $files = array();

    public function __construct($conn)
    {
        if (!is_numeric($len = stream_get_line($conn, 20, ':')))
            throw new RuntimeException("invalid protocol");

        $_headers = explode("\0", stream_get_line($conn, $len)); // getting headers
        $divider = stream_get_line($conn, 1); // ","

        $first = null;
        foreach ($_headers as $element) {
            if (null === $first) {
                $first = $element;
            } else {
                $this->headers[$first] = $element;
                $first = null;
            }
        }
        unset($_headers, $first);

        if (!isset($this->headers['SCGI']) or $this->headers['SCGI'] != '1')
            throw new RuntimeException("Reqest is not SCGI/1 Compliant");

        if (!isset($this->headers['CONTENT_LENGTH']))
            throw new RuntimeException("CONTENT_LENGTH header not present");

        if ($this->headers['CONTENT_LENGTH'] > 0) {
            if ($this->headers['CONTENT_LENGTH'] > self::IniString_to_Bytes(ini_get('post_max_size')))
                throw new RuntimeException("POST is larger than allowed by post_max_size");

            $this->body = stream_get_line($conn, $this->headers['CONTENT_LENGTH']);
        }

        unset($this->headers['SCGI'], $this->headers['CONTENT_LENGTH']);

        parse_str($this->headers['QUERY_STRING'], $this->get);

        if ($this->isPost()) {
            if (isset($this->headers['CONTENT_TYPE']) and strpos($this->headers['CONTENT_TYPE'], 'multipart/form-data') === 0) {
                $this->parseMultipart();
            } else {
                parse_str(urldecode($this->body), $this->post);
            }
        }
    }

    public function __destruct()
    {
        foreach ($this->files as $file) {
            if ($file['error'] == UPLOAD_ERR_OK and file_exists($file['tmp_name'])) {
                unlink($file['tmp_name']);
            }
        }
    }

    private function parseMultipart()
    {
        $vars_accu = array(); // place to accumulate parts of post-vars string

        $ct = $this->headers['CONTENT_TYPE'];
        $b = $this->body;

        $pos = strpos($ct, '=-') + 1;
        $boundary = '--'.substr($ct, $pos);
        $boundary_len = strlen($boundary);

        $pos = 0;
        while (substr($b, $pos + $boundary_len, 2) != '--') {
            // getting headers of part
            $h_start = $pos + $boundary_len + 2;
            $h_end = strpos($b, "\r\n\r\n", $h_start);

            $headers = array();
            foreach (explode("\r\n", substr($b, $h_start, $h_end - $h_start)) as $h_str) {
                $divider = strpos($h_str, ':');
                $headers[substr($h_str, 0, $divider)] = substr($h_str, $divider + 2);
            }

            if (!isset($headers['Content-Disposition']))
                throw new RuntimeException("Didn't find Content-disposition in one of the parts of multipart");

            // parsing dispositin-header of part
            $disposition = array();
            foreach (explode("; ", $headers['Content-Disposition']) as $d_part) {
                if ($d_part == 'form-data')
                    continue;

                $divider = strpos($d_part, '=');
                $disposition[substr($d_part, 0, $divider)] = substr($d_part, $divider + 2, -1);
            }

            // getting body of part
            $b_start = $h_end + 4;
            $b_end = strpos($b, "\r\n".$boundary, $b_start);

            $file_data = substr($b, $b_start, $b_end - $b_start);

            if (isset($disposition['filename'])) {
                $tmp_dir = ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir();

                // ToDo:
                //  UPLOAD_ERR_FORM_SIZE
                //  UPLOAD_ERR_PARTIAL (?)
                //  UPLOAD_ERR_NO_FILE (?)
                //  UPLOAD_ERR_EXTENSION

                if (empty($tmp_dir)) {
                    $this->files[$disposition['name']] = array(
                        'name' => '',
                        'type' => '',
                        'size' => 0,
                        'tmp_name' => '',
                        'error' => UPLOAD_ERR_NO_TMP_DIR<commit_msg>5.3 compatibility<commit_after><?php
namespace MFS::AppServer::SCGI;

class Request
{
    private $headers = array();
    private $body = null;
    private $get = array();
    private $post = array();
    private $files = array();

    public function __construct($conn)
    {
        if (!is_numeric($len = stream_get_line($conn, 20, ':')))
            throw new RuntimeException("invalid protocol");

        $_headers = explode("\0", stream_get_line($conn, $len)); // getting headers
        $divider = stream_get_line($conn, 1); // ","

        $first = null;
        foreach ($_headers as $element) {
            if (null === $first) {
                $first = $element;
            } else {
                $this->headers[$first] = $element;
                $first = null;
            }
        }
        unset($_headers, $first);

        if (!isset($this->headers['SCGI']) or $this->headers['SCGI'] != '1')
            throw new RuntimeException("Reqest is not SCGI/1 Compliant");

        if (!isset($this->headers['CONTENT_LENGTH']))
            throw new RuntimeException("CONTENT_LENGTH header not present");

        if ($this->headers['CONTENT_LENGTH'] > 0) {
            if ($this->headers['CONTENT_LENGTH'] > self::IniString_to_Bytes(ini_get('post_max_size')))
                throw new RuntimeException("POST is larger than allowed by post_max_size");

            // $this->body = stream_get_line($conn, $this->headers['CONTENT_LENGTH'], '');
            $this->body = stream_get_contents($conn, $this->headers['CONTENT_LENGTH']);

            if (strlen($this->body) != $this->headers['CONTENT_LENGTH']) {
                throw new RuntimeException("Didn't get all of the request: ".strlen($this->body).' of '.$this->headers['CONTENT_LENGTH']);
            }
        }

        unset($this->headers['SCGI'], $this->headers['CONTENT_LENGTH']);

        parse_str($this->headers['QUERY_STRING'], $this->get);

        if ($this->isPost()) {
            if (isset($this->headers['CONTENT_TYPE']) and strpos($this->headers['CONTENT_TYPE'], 'multipart/form-data') === 0) {
                $this->parseMultipart();
            } else {
                parse_str(urldecode($this->body), $this->post);
            }
        }
    }

    public function __destruct()
    {
        foreach ($this->files as $file) {
            if ($file['error'] == UPLOAD_ERR_OK and file_exists($file['tmp_name'])) {
                unlink($file['tmp_name']);
            }
        }
    }

    private function parseMultipart()
    {
        $vars_accu = array(); // place to accumulate parts of post-vars string

        $ct = $this->headers['CONTENT_TYPE'];
        $b = $this->body;

        file_put_contents('debug.bin', $b);

        $pos = strpos($ct, '=-') + 1;
        $boundary = '--'.substr($ct, $pos);
        $boundary_len = strlen($boundary);

        $pos = 0;
        while (substr($b, $pos + $boundary_len, 2) != '--') {
            // getting headers of part
            $h_start = $pos + $boundary_len + 2;
            $h_end = strpos($b, "\r\n\r\n", $h_start);

            $headers = array();
            foreach (explode("\r\n", substr($b, $h_start, $h_end - $h_start)) as $h_str) {
                $divider = strpos($h_str, ':');
                $headers[substr($h_str, 0, $divider)] = html_entity_decode(substr($h_str, $divider + 2), ENT_QUOTES, 'UTF-8');
            }

            if (!isset($headers['Content-Disposition']))
                throw new RuntimeException("Didn't find Content-disposition in one of the parts of multipart: ".var_export(array_keys($headers), true));

            // parsing dispositin-header of part
            $disposition = array();
            foreach (explode("; ", $headers['Content-Disposition']) as $d_part) {
                if ($d_part == 'form-data')
                    continue;

                $divider = strpos($d_part, '=');
                $disposition[substr($d_part, 0, $divider)] = substr($d_part, $divider + 2, -1);
            }

            // getting body of part
            $b_start = $h_end + 4;
            $b_end = strpos($b, "\r\n".$boundary, $b_start);

            if (false === $b_end) {
                throw new RuntimeException("Didn't find end of body :-/");
            }

            $file_data = substr($b, $b_start, $b_end - $b_start);

            if (isset($disposition['filename'])) {
                $tmp_dir = ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir();

                // ToDo:
                //  UPLOAD_ERR_FORM_SIZE
                //  UPLOAD_ERR_PARTIAL (?)
                //  UPLOAD_ERR_NO_FILE (?)
                //  UPLOAD_ERR_EXTENSION

                if (empty($tmp_dir)) {
                    $this->files[$disposition['name']] = array(
                        'name' => '',
                        'type' => '',
                        'size' => 0,
                        'tmp_name' => '',
                        'error' => UPLOAD_ERR_NO_TMP_DIR
{'fraction_non_alphanumeric': 0.1629267255131341, 'fraction_numerical': 0.00436031053918962, 'mean_word_length': 1.874961785386732, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 67, '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': '7218957', 'n_tokens_mistral': 2893, 'n_tokens_neox': 2750, 'n_words': 557}