2019-03-24 13:14:00 +00:00
|
|
|
from __future__ import print_function
|
2007-10-12 05:29:23 +00:00
|
|
|
|
2012-05-16 21:39:48 +00:00
|
|
|
import os
|
2021-03-18 15:00:49 +00:00
|
|
|
from io import BytesIO
|
2020-08-21 20:27:21 +00:00
|
|
|
from six.moves import urllib, http_client
|
|
|
|
import six
|
versioning: include an "appname" in the application version string in the versioning protocol, and make that appname be controlled by setup.py
It is currently hardcoded in setup.py to be 'allmydata-tahoe'. Ticket #556 is to make it configurable by a runtime command-line argument to setup.py: "--appname=foo", but I suddenly wondered if we really wanted that and at the same time realized that we don't need that for tahoe-1.3.0 release, so this patch just hardcodes it in setup.py.
setup.py inspects a file named 'src/allmydata/_appname.py' and assert that it contains the string "__appname__ = 'allmydata-tahoe'", and creates it if it isn't already present. src/allmydata/__init__.py import _appname and reads __appname__ from it. The rest of the Python code imports allmydata and inspects "allmydata.__appname__", although actually every use it uses "allmydata.__full_version__" instead, where "allmydata.__full_version__" is created in src/allmydata/__init__.py to be:
__full_version__ = __appname + '-' + str(__version__).
All the code that emits an "application version string" when describing what version of a protocol it supports (introducer server, storage server, upload helper), or when describing itself in general (introducer client), usese allmydata.__full_version__.
This fixes ticket #556 at least well enough for tahoe-1.3.0 release.
2009-02-12 00:18:16 +00:00
|
|
|
import allmydata # for __full_version__
|
2007-10-12 05:29:23 +00:00
|
|
|
|
2010-07-12 00:30:15 +00:00
|
|
|
from allmydata.util.encodingutil import quote_output
|
2010-06-07 01:02:15 +00:00
|
|
|
from allmydata.scripts.common import TahoeError
|
2012-03-31 22:53:45 +00:00
|
|
|
from socket import error as socket_error
|
2010-06-07 01:02:15 +00:00
|
|
|
|
2007-10-12 05:29:23 +00:00
|
|
|
# copied from twisted/web/client.py
|
|
|
|
def parse_url(url, defaultPort=None):
|
|
|
|
url = url.strip()
|
2020-08-21 20:27:21 +00:00
|
|
|
parsed = urllib.parse.urlparse(url)
|
2007-10-12 05:29:23 +00:00
|
|
|
scheme = parsed[0]
|
2020-08-21 20:27:21 +00:00
|
|
|
path = urllib.parse.urlunparse(('','')+parsed[2:])
|
2007-10-12 05:29:23 +00:00
|
|
|
if defaultPort is None:
|
|
|
|
if scheme == 'https':
|
|
|
|
defaultPort = 443
|
|
|
|
else:
|
|
|
|
defaultPort = 80
|
|
|
|
host, port = parsed[1], defaultPort
|
|
|
|
if ':' in host:
|
|
|
|
host, port = host.split(':')
|
|
|
|
port = int(port)
|
|
|
|
if path == "":
|
|
|
|
path = "/"
|
|
|
|
return scheme, host, port, path
|
|
|
|
|
2012-09-04 22:44:51 +00:00
|
|
|
class BadResponse(object):
|
|
|
|
def __init__(self, url, err):
|
|
|
|
self.status = -1
|
|
|
|
self.reason = "Error trying to connect to %s: %s" % (url, err)
|
2015-11-12 23:16:28 +00:00
|
|
|
self.error = err
|
2017-12-30 23:23:43 +00:00
|
|
|
def read(self, length=0):
|
2012-09-04 22:44:51 +00:00
|
|
|
return ""
|
|
|
|
|
2007-10-12 05:29:23 +00:00
|
|
|
|
2021-03-18 17:29:56 +00:00
|
|
|
def do_http(method, url, body=b""):
|
2021-03-18 15:00:49 +00:00
|
|
|
if isinstance(body, bytes):
|
|
|
|
body = BytesIO(body)
|
2020-08-21 20:27:21 +00:00
|
|
|
elif isinstance(body, six.text_type):
|
2009-02-23 00:31:06 +00:00
|
|
|
raise TypeError("do_http body must be a bytestring, not unicode")
|
2007-10-12 05:29:23 +00:00
|
|
|
else:
|
2008-05-22 00:31:19 +00:00
|
|
|
# We must give a Content-Length header to twisted.web, otherwise it
|
|
|
|
# seems to get a zero-length file. I suspect that "chunked-encoding"
|
|
|
|
# may fix this.
|
2007-10-12 05:29:23 +00:00
|
|
|
assert body.tell
|
|
|
|
assert body.seek
|
|
|
|
assert body.read
|
|
|
|
scheme, host, port, path = parse_url(url)
|
|
|
|
if scheme == "http":
|
2020-08-21 20:27:21 +00:00
|
|
|
c = http_client.HTTPConnection(host, port)
|
2007-10-12 05:29:23 +00:00
|
|
|
elif scheme == "https":
|
2020-08-21 20:27:21 +00:00
|
|
|
c = http_client.HTTPSConnection(host, port)
|
2007-10-12 05:29:23 +00:00
|
|
|
else:
|
|
|
|
raise ValueError("unknown scheme '%s', need http or https" % scheme)
|
|
|
|
c.putrequest(method, path)
|
|
|
|
c.putheader("Hostname", host)
|
2009-02-13 05:37:38 +00:00
|
|
|
c.putheader("User-Agent", allmydata.__full_version__ + " (tahoe-client)")
|
2009-12-27 19:58:28 +00:00
|
|
|
c.putheader("Accept", "text/plain, application/octet-stream")
|
2007-10-12 05:29:23 +00:00
|
|
|
c.putheader("Connection", "close")
|
|
|
|
|
|
|
|
old = body.tell()
|
2012-05-16 21:39:48 +00:00
|
|
|
body.seek(0, os.SEEK_END)
|
2007-10-12 05:29:23 +00:00
|
|
|
length = body.tell()
|
|
|
|
body.seek(old)
|
|
|
|
c.putheader("Content-Length", str(length))
|
|
|
|
|
2012-03-31 22:53:45 +00:00
|
|
|
try:
|
|
|
|
c.endheaders()
|
2019-03-28 11:45:28 +00:00
|
|
|
except socket_error as err:
|
2012-09-04 22:44:51 +00:00
|
|
|
return BadResponse(url, err)
|
|
|
|
|
2007-10-12 05:29:23 +00:00
|
|
|
while True:
|
|
|
|
data = body.read(8192)
|
|
|
|
if not data:
|
|
|
|
break
|
|
|
|
c.send(data)
|
|
|
|
|
|
|
|
return c.getresponse()
|
2008-05-20 02:28:50 +00:00
|
|
|
|
2010-06-07 01:02:15 +00:00
|
|
|
|
|
|
|
def format_http_success(resp):
|
|
|
|
return "%s %s" % (resp.status, quote_output(resp.reason, quotemarks=False))
|
|
|
|
|
|
|
|
def format_http_error(msg, resp):
|
|
|
|
return "%s: %s %s\n%s" % (msg, resp.status, quote_output(resp.reason, quotemarks=False),
|
|
|
|
quote_output(resp.read(), quotemarks=False))
|
|
|
|
|
2008-05-20 02:28:50 +00:00
|
|
|
def check_http_error(resp, stderr):
|
|
|
|
if resp.status < 200 or resp.status >= 300:
|
2019-03-24 13:14:00 +00:00
|
|
|
print(format_http_error("Error during HTTP request", resp), file=stderr)
|
2008-05-20 02:28:50 +00:00
|
|
|
return 1
|
2010-06-07 01:02:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
class HTTPError(TahoeError):
|
|
|
|
def __init__(self, msg, resp):
|
2010-06-07 17:47:14 +00:00
|
|
|
TahoeError.__init__(self, format_http_error(msg, resp))
|