Dev (Back & Front)ARTIGO

Criando um servidor HTTP (Web) com Python

Hoje contamos com alguns servidores HTTP (web) tais como Apache, lighttpd, Nginx, TUX, Cherokee e outros. O desenvolvimento deste artigo é para explicar melhor como trabalhar com Python e desenvolver coisas novas para as necessidades do dia a dia.

Vou explicar qual foi a necessidade do meu cliente para que eu escrevesse este software:

O Cliente tinha um sistema rodando com Ruby On Rails e o desenvolvedor como não sabia configura o Ruby para processar juntamente com o servidor web (Apache); ele rodou o servidor web do próprio Ruby On Rails com a porta 4000.

Como teria que mexer no sistema e não sou programador Ruby, fiz uma contra-proposta para desenvolver o sistema novamente (porque tinha muitos erros) em Python (pois é a linguagem que eu sei programar). O cliente aceitou e dei start no desenvolvimento.

Ao terminar o software, queria que ficasse transparente para o usuário final (para ele acessar da mesma forma que acessava); testei alguns servidores web como Nginx e lighttpd, só que ele estava comendo recursos desnecessário no servidor, lembrando que eu só queria colocar um HTML com uma meta tag para redirecionar para o sistema novo. Bom, resolvi desenvolver uma solução própria em Python, e achei a biblioteca BaseHTTPServer.

A estrutura que montei é a seguinte:

avelino@program-8:~/python/httpd$ ls<br />htdocs  httpd.py

O daemon que vamos usar é o httpd.py, vamos para a parte de código.

Vamos usar as seguintes bibliotecas:

import sys,os<br />import string,cgi,time<br />from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

Vamos escrever uma classe para tratar o POST e GET (Não usei o POST na minha necessidade, mas para quem quer fazer um servidorHTTP simples pode precisar):

class http(BaseHTTPRequestHandler): <br /><br />    def do_GET(self):<br />        try:<br />            if self.path.endswith(".html"):<br />                f = open(DocumentRoot + self.path)<br />                self.send_response(200)<br />                self.send_header('Content-type','text/html')<br />                self.end_headers()<br />                self.wfile.write(f.read())<br />                f.close()<br />                return<br /><br />            if self.path.endswith(".esp"):<br />                self.send_response(200)<br />                self.send_header('Content-type','text/html')<br />                self.end_headers()<br />                self.wfile.write("hey, today is the" + str(time.localtime()[7]))<br />                self.wfile.write(" day in the year " + str(time.localtime()[0]))<br />                return<br /><br />            return<br /><br />        except IOError:<br />            self.send_error(404,'File Not Found: %s' % self.path)<br /><br />    def do_POST(self):<br />        global rootnode<br />        try:<br />            ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))<br />            if ctype == 'multipart/form-data':<br />                query=cgi.parse_multipart(self.rfile, pdict)<br />            self.send_response(301)<br /><br />            self.end_headers()<br />            upfilecontent = query.get('upfile')<br />            print "filecontent", upfilecontent[0]<br />            self.wfile.write("<html>Post OK. <br /><br />");<br />            self.wfile.write(upfilecontent[0]);<br />            self.wfile.write("</html>")<br /><br />        except:<br /><br />            pass

Os nomes das def do_GET e do_POST são padrões do BaseHTTPRequestHandler. O do_GET usamos para renderização de qualquer arquivo. Por exemplo, temos uma index.html em nossa pasta htdocs, ele vai trabalhar pegando o arquivo index.html (/home/avelino/python/httpd/htdocs/index.html) e renderizando o html dele.

Vou criar uma função para chamar as declarações da classe:

def main(NameVirtualHost):<br /><br />    try:<br />        virtualhost = string.split(NameVirtualHost,":")<br />        if virtualhost[0] == "*":<br />            virtualhost[0] = ""<br />         <br />        server = HTTPServer((virtualhost[0], int(virtualhost[1])), http)<br />        print 'Start server HTTP IN %s' % NameVirtualHost<br />        server.serve_forever()<br /><br />    except KeyboardInterrupt:<br />        print 'Shutting down server HTTP'<br />        server.socket.close()

A var NameVirtualHost é onde vamos passar IP e PORTA para o servidor HTTP, exemplo.

Liberar só para acesso local: localhost:8000
Liberar para qualquer IP que estiver configurado na maquina: *:8000

Na biblioteca BaseHTTPServer temos uma função onde o mesmo server, para rodar o servidor, recebe dois parâmetros: o primeiro é IP/PORTA, e o segundo é a classe onde estão os métodos (GET, POST etc) que, no nosso caso, é a classe html.

Agora vamos chamar a def que fizemos:

if __name__ == '__main__':<br />    DocumentRoot = "%s/htdocs/" % os.path.realpath(os.path.dirname(__file__))<br />    PORT = "8000"<br />    HOST = "localhost"<br /><br />    try :<br />        main(sys.argv[1])<br />    except :<br />        main("%s:%s" % (HOST,PORT))
  • DocumentRoot = Pega a pasta local que estamos e concatena com “/htdocs/”
  • PORT = Porta padrão do servidor
  • HOST = Host padrão do servidor

Temos uma TRY que vamos tratar o que vem por parâmetro (argv), caso ele tenha valores errados chamará o HOST/PORT declarado do software.

Rodando o servidor:

avelino@program-8:~/python/httpd$ python httpd.py *:8000<br />Start server HTTP IN *:8000

HTTP_in_Python

Agora, para matar o processo, é como qualquer outro:

avelino@program-8:~/python/httpd$ python httpd.py localhost:8000<br />Start server >HTTP IN localhost:8000<br />localhost - - [14/Sep/2010 09:42:05] "GET /index.html >HTTP/1.1" 200 -<br />localhost - - [14/Sep/2010 09:42:54] "GET /index.html >HTTP/1.1" 200 -<br />^CShutting down server >HTTP<br />avelino@program-8:~/python/httpd$ 

Se olharmos o LOG acima, ele mostra tudo que foi processado com LOG de Apache ou qualquer outro servidor HTTP.

Fundador da nuveo.ai/levpay (com Banco Rendimento) e Matemático pelo IME-USP. Trabalha no dia a dia com inteligência artificial, rede neural (usando técnicas de machine e deep learning) e visão computacional. Com a necessidade de processar grandes volumes de dados o seu foco de pesquisa e desenvolvimento incluem Go, Racket/Clojure (apaixonado por dialeto lisp), Python, sistemas distribuídos, concorrência e já passou por diversos projetos Open Source https://github.com/avelino.

Ver perfil