Cobra/MyHttpServer.cobra

Материал из Викиучебника — открытых книг для открытого мира
use System
use System.Collections
use System.IO
use System.Net
use System.Net.Sockets
use System.Threading

class HttpProcessor
        var _socket as TcpClient
        pro socket as TcpClient
                get
                        return _socket
                set
                        _socket = value

        var _srv as HttpServer
        pro srv as HttpServer
                get
                        return _srv
                set
                        _srv = value

        var __inputStream as Stream is private

        var _outputStream as StreamWriter
        pro outputStream as StreamWriter
                get
                        return _outputStream
                set
                        _outputStream = value

        var _http_method as String = ''
        pro http_method as String
                get
                        return _http_method
                set
                        _http_method = value
        var _http_url as String = ''
        pro http_url as String
                get
                        return _http_url
                set
                        _http_url = value
        var _http_protocol_version_string as String = ''
        pro http_protocol_version_string as String
                get
                        return _http_protocol_version_string
                set
                        _http_protocol_version_string = value
        var _httpHeaders as Hashtable = Hashtable()
        pro httpHeaders as Hashtable
                get
                        return _httpHeaders
                set
                        _httpHeaders = value

        const MAX_POST_SIZE = 10 * 1024 * 1024
                is private
        const BUF_SIZE = 4096
                is private

        cue init(socket as TcpClient, srv as HttpServer)
                base.init
                .socket = socket
                .srv = srv
                __inputStream = BufferedStream(_socket.getStream)
                _outputStream = StreamWriter(BufferedStream(_socket.getStream))

        def streamReadLine(inputStream as Stream) as String
                data = StringBuilder()
                while true
                        next_char = inputStream.readByte
                        if next_char == Convert.toByte(c'\n'), break
                        if next_char == Convert.toByte(c'\r'), continue
                        if next_char == -1
                                Thread.sleep(1)
                                continue
                        data.append(Convert.toChar(next_char))
                return data.toString

        def parseRequest is public
                request = .streamReadLine(__inputStream)
                tokens = request.split(' ')
                if tokens.length <> 3, throw Exception("invalid http request line")
                _http_method = tokens[0].toUpper
                _http_url = tokens[1]
                _http_protocol_version_string = tokens[2]

                print 'starting: [request]'

        def readHeaders is public
                print 'readHeaders()'
                line as String?
                while (line = .streamReadLine(__inputStream)) <> nil
                        if line.equals('')
                                print 'got headers'
                                return

                        separator = line.indexOf(c':')
                        if separator == -1, throw Exception('invalid http header line: [line]')
                        name = line.substring(0, separator)
                        pos = separator + 1
                        while (pos < line.length) and (line[pos] == c' '), pos += 1

                        value = line.substring(pos, line.length - pos)
                        print 'header: [name]:[value]'
                        _httpHeaders[name] = value

        def handleGETRequest is public
                _srv.handleGETRequest(this)

        def process is public
                print 'process()'
                try
                        .parseRequest
                        .readHeaders
                        if _http_method.equals('GET'), .handleGETRequest
                        else if _http_method.equals('POST'), .handlePOSTRequest
                catch e as Exception
                        print 'Exception: [e.toString]'
                        .writeFailure
                _outputStream.flush
                _socket.close
                Thread.sleep(0)

        def handlePOSTRequest is public
                print 'get post data start'
                content_len = 0
                ms = MemoryStream()
                if _httpHeaders.containsKey("Content-Length")
                        content_len = Convert.toInt32(_httpHeaders["Content-Length"])
                        if content_len > .MAX_POST_SIZE
                                throw Exception(String.format(
                                        "POST Content-Length({0}) too big for this simple server",
                                        content_len)
                                )
                        buf = uint8[](.BUF_SIZE)
                        to_read = content_len
                        while to_read > 0
                                print 'starting Read, to_read=[to_read]'

                                numread = __inputStream.read(buf, 0, Math.min(.BUF_SIZE, to_read))
                                print 'read finished, numread=[numread]'
                                if numread == 0
                                        if to_read == 0, break
                                        else, throw Exception('client disconnected during post')
                                to_read -= numread
                                ms.write(buf, 0, numread)
                        ms.seek(0, SeekOrigin.Begin)
                print 'get post data end'
                _srv.handlePOSTRequest(this, StreamReader(ms))

        def writeSuccess is public
                _outputStream.write('HTTP/1.0 200 OK\n')
                _outputStream.write('Content-Type: text/html\n')
                _outputStream.write('Connection: close\n')
                _outputStream.write('\n')

        def writeFailure is public
                _outputStream.write('HTTP/1.0 404 File not found\n')
                _outputStream.write('Connection: close\n')
                _outputStream.write('\n')

class HttpServer is abstract

        var _port as int is protected
        var _listener as TcpListener?
        var _is_active = true

        cue init(port as int)
                base.init
                _port = port

        def listen is public
                #ipAddress = Dns.getHostEntry("localhost").addressList[0]
                #_listener = TcpListener(ipAddress, _port)
                _listener = TcpListener(IPAddress.any, _port)
                _listener.start
                while _is_active
                        s = _listener.acceptTcpClient
                        processor = HttpProcessor(s, this)
                        thread = Thread(ThreadStart(ref processor.process))
                        thread.start
                        Thread.sleep(1)

        def handleGETRequest(p as HttpProcessor) is abstract

        def handlePOSTRequest(p as HttpProcessor, inputData as StreamReader) is abstract

class MyHttpServer inherits HttpServer
        cue init(port as int)
                base.init(port)

        def handleGETRequest(p as HttpProcessor) is public, override
                print 'request: [p.http_url]'
                p.writeSuccess
                p.outputStream.writeLine('<html><body><h1>test server</h1>')
                p.outputStream.writeLine('Current Time: [DateTime.now.toString]')
                p.outputStream.writeLine('url : [p.http_url]')
                p.outputStream.writeLine('<form method=post action=/form>')
                p.outputStream.writeLine('<input type=text name=foo value=foovalue>')
                p.outputStream.writeLine('<input type=submit name=bar value=barvalue>')
                p.outputStream.writeLine('</form>')

        def handlePOSTRequest(p as HttpProcessor, inputData as StreamReader) is public, override
                print 'POST request: [p.http_url]'
                data = inputData.readToEnd
                p.outputStream.writeLine('<html><body><h1>test server</h1>')
                p.outputStream.writeLine('<a href=/test>return</a><p>')
                p.outputStream.writeLine('postbody: <pre>[data]</pre>')

class Program
        def main
                if CobraCore.commandLineArgs.count > 1
                        arg = CobraCore.commandLineArgs[1]
                        if arg.length > 0, httpServer = MyHttpServer(Convert.toInt32(arg))
                        else, httpServer = MyHttpServer(8080)
                else
                        httpServer = MyHttpServer(8080)
                thread = Thread(ThreadStart(ref httpServer.listen))
                thread.start