Замечательный интерфейс URLFetch позволяет выполнять загрузку страниц по протоколам HTTP и HTTPS. Может ли App Engine сама себя вести как клиентский браузер? Конечно может!
Ниже мы рассмотрим пример создания приложения, которое будет загружать заданный пользователем сайт и передавать ему. Наиболее очевидный способ его применения: персональный анонимный прокси сервер. Вы будете самостоятельно управлять своей приватностью, а, при необходимости, быть уверенным, что все логи посещений этого прокси сервера были уничтожены.
Сам код очень тривиален:
# coding=UTF-8
# -*- coding: utf-8 -*-
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.api import urlfetch
import re
import logging
allowed_headers = ('content-type', 'Content-Type', 'Set-Cookie', 'set-cookie')
HOST = 'gproxyru.appspot.com'
def add_header(self, header, info):
if header == 'Content-Type':
self.response.headers['Content-Type'] = info
return
if (header == 'Set-Cookie') or (header == 'set-cookie'):
cookies = re.split(', ', info)
for cookie in cookies:
# Убираем требования по безопасности
cookie = re.sub('secure; ', '', cookie)
self.response.headers.add_header('Set-Cookie', cookie)
return
self.response.headers.add_header(header, info)
logging.error(header + ': '+ info)
def process_page(self, url, method, mode='http'):
hm = re.findall('//(.*)/', url)
if len(hm) == 0:
hostname = re.findall('//(.*)', url)[0]
else:
hostname = hm[0]
result = urlfetch.fetch(url, method=urlfetch.GET)
# меняем все ссылки в документе http:// на http://HOST/http/ и тому подобное
content = re.sub('http://', 'http://' + HOST + '/http/', result.content)
content = re.sub('https://', 'http://' + HOST + '/https/', content)
# относительные источники изображений
content = re.sub(' src=/', ' src=/'+ mode +'/' + hostname + '/', content)
content = re.sub(' src="/', ' src="/'+ mode +'/' + hostname + '/', content)
# href для CSS и A
content = re.sub(' href=/', ' href=/'+ mode +'/' + hostname + '/', content)
content = re.sub(' href="/', ' href="/'+ mode +'/' + hostname + '/', content)
# ссылки для форм
content = re.sub(' action=/', ' action=/'+ mode +'/' + hostname + '/', content)
content = re.sub(' action="/', ' action="/'+ mode +'/' + hostname + '/', content)
self.response.out.write(content)
for header in result.headers.keys():
if header in allowed_headers:
add_header(self, header, result.headers[header])
class Http(webapp.RequestHandler):
def url(self):
res = re.search('/http/(.*)', self.request.url)
if res != None:
return 'http://' + res.group(1)
def get(self):
process_page(self, self.url(), urlfetch.GET)
def post(self):
process_page(self, self.url(), urlfetch.POST)
class Https(webapp.RequestHandler):
def url(self):
res = re.search('/https/(.*)', self.request.url)
if res != None:
return 'https://' + res.group(1)
def get(self):
process_page(self, self.url(), urlfetch.GET, mode='https')
def post(self):
process_page(self, self.url(), urlfetch.POST, mode='https')
class MainPage(webapp.RequestHandler):
def get(self):
self.response.out.write("""<h3>Простенький анонимный прокси-сервер от Google</h3>
<form action='/go'>
Введите сайт, например <b>www.google.ru</b>: <input name=q size=70>
<input type=submit value="Перейти">
</form>
""")
class Q(webapp.RequestHandler):
def get(self):
q = self.request.get('q')
if re.match('http://', q):
self.redirect('/http/' + q[7:])
return
if re.match('https://', q):
self.redirect('/https/' + q[8:])
return
self.redirect('/http/' + q)
def main():
application = webapp.WSGIApplication(
[('/', MainPage),
('/go', Q),
('/http/.*', Http),
('/https/.*', Https)
],
debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()
Посмотреть работу вживую можно на сайте gproxyru.appspot.com. Ниже приведен пример, как Mail.Ru определил наше местоположение как Вашингтон:

Недостатки:
- хотя сайты по протоколу HTTPS прекрасно открываются, передача данных на участке от клиента до датацентра Google проходит без шифрации
- на данный момент никак не реализована работа с куками (хотя для анонимности, может быть это и хорошо
)
- замена ссылок в коде страницы для быстроты реакции производится регулярными выражениями, возможно, это не самый правильный способ
- существуют мелкие недочеты в виде нарушения работы кода JavaScript, либо, наоборот, невозможности скрыть IP адрес от таких скриптов.