Интерфейс Source с Python и urllib2

Посмотрите на документацию Языка программирования Objective C относительно ADC

Под разделом по Определению Класса | Интерфейс Класса, который это описывает, почему это сделано:

@class директива минимизирует объем кода, замеченный компилятором и компоновщиком, и является поэтому самым простым способом дать предописание имени класса. Будучи простым, это избегает потенциальных проблем, которые могут идти с импортом файлов, которые импортируют все еще другие файлы. Например, если один класс объявляет переменную экземпляра со статическим контролем типов другого класса, и их два интерфейсных файла импортируют друг друга, никакой класс не может скомпилировать правильно.

я надеюсь, что это помогает.

31
задан jonasl 19 July 2009 в 17:05
поделиться

3 ответа

Unfortunately the stack of standard library modules in use (urllib2, httplib, socket) is somewhat badly designed for the purpose -- at the key point in the operation, HTTPConnection.connect (in httplib) delegates to socket.create_connection, which in turn gives you no "hook" whatsoever between the creation of the socket instance sock and the sock.connect call, for you to insert the sock.bind just before sock.connect that is what you need to set the source IP (I'm evangelizing widely for NOT designing abstractions in such an airtight, excessively-encapsulated way -- I'll be speaking about that at OSCON this Thursday under the title "Zen and the Art of Abstraction Maintenance" -- but here your problem is how to deal with a stack of abstractions that WERE designed this way, sigh).

When you're facing such problems you only have two not-so-good solutions: either copy, paste and edit the misdesigned code into which you need to place a "hook" that the original designer didn't cater for; or, "monkey-patch" that code. Neither is GOOD, but both can work, so at least let's be thankful that we have such options (by using an open-source and dynamic language). In this case, I think I'd go for monkey-patching (which is bad, but copy and paste coding is even worse) -- a code fragment such as:

import socket
true_socket = socket.socket
def bound_socket(*a, **k):
    sock = true_socket(*a, **k)
    sock.bind((sourceIP, 0))
    return sock
socket.socket = bound_socket

Depending on your exact needs (do you need all sockets to be bound to the same source IP, or...?) you could simply run this before using urllib2 normally, or (in more complex ways of course) run it at need just for those outgoing sockets you DO need to bind in a certain way (then each time restore socket.socket = true_socket to get out of the way for future sockets yet to be created). The second alternative adds its own complications to orchestrate properly, so I'm waiting for you to clarify whether you do need such complications before explaining them all.

AKX's good answer is a variant on the "copy / paste / edit" alternative so I don't need to expand much on that -- note however that it doesn't exactly reproduce socket.create_connection in its connect method, see the source here (at the very end of the page) and decide what other functionality of the create_connection function you may want to embody in your copied/pasted/edited version if you decide to go that route.

46
ответ дан 27 November 2019 в 21:41
поделиться

Кажется, это работает.

import urllib2, httplib, socket

class BindableHTTPConnection(httplib.HTTPConnection):
    def connect(self):
        """Connect to the host and port specified in __init__."""
        self.sock = socket.socket()
        self.sock.bind((self.source_ip, 0))
        if isinstance(self.timeout, float):
            self.sock.settimeout(self.timeout)
        self.sock.connect((self.host,self.port))

def BindableHTTPConnectionFactory(source_ip):
    def _get(host, port=None, strict=None, timeout=0):
        bhc=BindableHTTPConnection(host, port=port, strict=strict, timeout=timeout)
        bhc.source_ip=source_ip
        return bhc
    return _get

class BindableHTTPHandler(urllib2.HTTPHandler):
    def http_open(self, req):
        return self.do_open(BindableHTTPConnectionFactory('127.0.0.1'), req)

opener = urllib2.build_opener(BindableHTTPHandler)
opener.open("http://google.com/").read() # Will fail, 127.0.0.1 can't reach google.com.

Однако вам нужно придумать какой-нибудь способ параметризации "127.0.0.1" там.

24
ответ дан 27 November 2019 в 21:41
поделиться

Я подумал, что займусь немного лучшей версией обезьяньего патча. Если вам нужно установить разные параметры порта для некоторых сокетов или вы используете что-то вроде SSL, подклассы сокета, следующий код работает немного лучше.

_ip_address = None
def bind_outgoing_sockets_to_ip(ip_address):
    """This binds all python sockets to the passed in ip address"""
    global _ip_address
    _ip_address = ip_address

import socket
from socket import socket as s

class bound_socket(s):
    def connect(self, *args, **kwargs):
        if self.family == socket.AF_INET:
            if self.getsockname()[0] == "0.0.0.0" and _ip_address:                
                self.bind((_ip_address, 0))
        s.connect(self, *args, **kwargs)
socket.socket = bound_socket

Вам нужно привязать сокет при подключении только в том случае, если вам нужно запустить что-то вроде веб-сервера в том же процессе, который должен привязаться к другому IP-адресу.

2
ответ дан 27 November 2019 в 21:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: