
header "sys" as sys

use net
use utils
use fs

+ class Connection {
    ~ worker: Worker
    ~ netcon: net:Connection
    ~ fd: FD

    fn handle() {

        // Settings
        let worker = this.worker
        let server = worker.server
        // let max_hs = server.max_request_header_size
        // let max_bs = server.max_request_body_size
        // let max_sbs = server.max_server_wide_body_size

        let handler = server.handler
        let fast_handler = server.fast_handler

        let input = utils:ByteBuffer.new(2048)
        let context = Context.new(input)
        let res = ResponseWriter{}

        while true {

            parse_http(input, context, false) ! {
                // Check if we need more data
                if error_is(E, incomplete) {
                    let bytes = this.read_more(input) ! {
                        break
                    }
                    if bytes > 0 : continue
                } else if error_is(E, http413) {
                    this.send_error(413, res)
                } else {
                    this.send_error(400, res)
                }
                break
            }

            // Handle request

            // Check static dirs
            let is_file = false
            if server.static_dirs.length > 0 && context.path.length > 0 {
                let path : String = context.path
                if !path.contains("..") {
                    each server.static_dirs as dir {
                        let fullpath : String = fs:resolve(fs:add(dir, path))
                        if fs:is_file(fullpath) && fullpath.starts_with(dir) {
                            res.send_file(fullpath)
                            is_file = true
                            break
                        }
                    }
                }
            }

            // Response
            if !is_file {
                if isset(fast_handler) {
                    fast_handler(context, res)
                    if !res.responded {
                        this.send_error(400, res)
                    }
                } else {
                    let request = Request {
                        method: context.method
                        path: context.path
                        header_data: context.header_data
                        query_string: context.query_string
                        body_data: context.body
                    }
                    let resp = handler(request)
                    res.send_response(resp)
                }
            }

            // Reset
            context.reset()

            // Clear input
            let reached_end = context.parsed_index >= input.length
            if context.parsed_index > 128 {
                input.clear_until(context.parsed_index)
                context.parsed_index = 0
            }

            // Flush output
            if res.should_send() || reached_end {
                res.send_bytes(this.netcon) ! { break }
                res.reset()
            }
            //
            res.allow_new_response()
        }

        res.reset()
        context.reset()
        input.clear()

        this.close()
        worker.connections--
        // atomic(server.connections - 1)
    }

    fn read_more(buffer: utils:ByteBuffer) uint !err {
        let bytes = this.netcon.recv(buffer, 65535) ! {
            throw err
        }
        return bytes
    }

    fn send_error(code: uint, res: ResponseWriter) {
        res.send_status(code)
        res.send_bytes(this.netcon) ! {
            return
        }
    }

    + fn close() {
        this.netcon.close()
    }
}


