Skip to content

Commit 7b935a3

Browse files
committed
Merge pull request #118 from qiniu/develop
Release 6.1.6
2 parents d3b4cf6 + b812e99 commit 7b935a3

12 files changed

+151
-63
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
## CHANGE LOG
22

3+
### v6.1.6
4+
5+
2014-06-30 issue [#118](https://github.com/qiniu/python-sdk/pull/118)
6+
- [#108] 增加限定用户上传类型字段
7+
- [#110] 0字节文件上传支持
8+
- [#114] 支持GAE
9+
- [#115] 请求完成后清除http header
10+
- [#120] 增加第二个up host重试
11+
312
### v6.1.5
413

514
2014-04-28 issue [#105](https://github.com/qiniu/python-sdk/pull/105)

qiniu/auth/digest.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ def __init__(self, host, mac=None):
5555
super(Client, self).__init__(host)
5656
self.mac = mac
5757

58-
def round_tripper(self, method, path, body):
58+
def round_tripper(self, method, path, body, header={}):
5959
token = self.mac.sign_request(
60-
path, body, self._header.get("Content-Type"))
61-
self.set_header("Authorization", "QBox %s" % token)
62-
return super(Client, self).round_tripper(method, path, body)
60+
path, body, header.get("Content-Type"))
61+
header["Authorization"] = "QBox %s" % token
62+
return super(Client, self).round_tripper(method, path, body, header)

qiniu/auth/up.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ def __init__(self, up_token, host=None):
1414
self.up_token = up_token
1515
super(Client, self).__init__(host)
1616

17-
def round_tripper(self, method, path, body):
18-
self.set_header("Authorization", "UpToken %s" % self.up_token)
19-
return super(Client, self).round_tripper(method, path, body)
17+
def round_tripper(self, method, path, body, header={}):
18+
header["Authorization"] = "UpToken %s" % self.up_token
19+
return super(Client, self).round_tripper(method, path, body, header)

qiniu/conf.py

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
RS_HOST = "rs.qbox.me"
77
RSF_HOST = "rsf.qbox.me"
88
UP_HOST = "up.qiniu.com"
9+
UP_HOST2 = "up.qbox.me"
910

1011
from . import __version__
1112
import platform

qiniu/io.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ def put(uptoken, key, data, extra=None):
5252
files = [
5353
{'filename': fname, 'data': data, 'mime_type': extra.mime_type},
5454
]
55-
return rpc.Client(conf.UP_HOST).call_with_multipart("/", fields, files)
55+
ret, err, code = rpc.Client(conf.UP_HOST).call_with_multipart("/", fields, files)
56+
if err is None or code == 571 or code == 614 or code == 301:
57+
return ret, err
58+
59+
ret, err, code = rpc.Client(conf.UP_HOST2).call_with_multipart("/", fields, files)
60+
return ret, err
5661

5762

5863
def put_file(uptoken, key, localfile, extra=None):

qiniu/resumable_io.py

+28-14
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,35 @@ def put_file(uptoken, key, localfile, extra):
6666
""" 上传文件 """
6767
f = open(localfile, "rb")
6868
statinfo = os.stat(localfile)
69-
ret = put(uptoken, key, f, statinfo.st_size, extra)
69+
ret, err = put(uptoken, key, f, statinfo.st_size, extra)
7070
f.close()
71-
return ret
71+
return ret, err
7272

7373

7474
def put(uptoken, key, f, fsize, extra):
7575
""" 上传二进制流, 通过将data "切片" 分段上传 """
7676
if not isinstance(extra, PutExtra):
7777
print("extra must the instance of PutExtra")
7878
return
79+
host = conf.UP_HOST
80+
try:
81+
ret, err, code = put_with_host(uptoken, key, f, fsize, extra, host)
82+
if err is None or code == 571 or code == 614 or code == 301:
83+
return ret, err
84+
except:
85+
pass
7986

87+
ret, err, code = put_with_host(uptoken, key, f, fsize, extra, conf.UP_HOST2)
88+
return ret, err
89+
90+
91+
def put_with_host(uptoken, key, f, fsize, extra, host):
8092
block_cnt = block_count(fsize)
8193
if extra.progresses is None:
8294
extra.progresses = [None] * block_cnt
8395
else:
8496
if not len(extra.progresses) == block_cnt:
85-
return None, err_invalid_put_progress
97+
return None, err_invalid_put_progress, 0
8698

8799
if extra.try_times is None:
88100
extra.try_times = _try_times
@@ -97,27 +109,29 @@ def put(uptoken, key, f, fsize, extra):
97109
read_length = fsize - i * _block_size
98110
data_slice = f.read(read_length)
99111
while True:
100-
err = resumable_block_put(data_slice, i, extra, uptoken)
112+
err = resumable_block_put(data_slice, i, extra, uptoken, host)
101113
if err is None:
102114
break
103115

104116
try_time -= 1
105117
if try_time <= 0:
106-
return None, err_put_failed
118+
return None, err_put_failed, 0
107119
print err, ".. retry"
108120

109-
mkfile_client = auth_up.Client(uptoken, extra.progresses[-1]["host"])
110-
return mkfile(mkfile_client, key, fsize, extra)
121+
mkfile_host = extra.progresses[-1]["host"] if block_cnt else host
122+
mkfile_client = auth_up.Client(uptoken, mkfile_host)
123+
124+
return mkfile(mkfile_client, key, fsize, extra, host)
111125

112126

113-
def resumable_block_put(block, index, extra, uptoken):
127+
def resumable_block_put(block, index, extra, uptoken, host):
114128
block_size = len(block)
115129

116-
mkblk_client = auth_up.Client(uptoken, conf.UP_HOST)
130+
mkblk_client = auth_up.Client(uptoken, host)
117131
if extra.progresses[index] is None or "ctx" not in extra.progresses[index]:
118132
crc32 = gen_crc32(block)
119133
block = bytearray(block)
120-
extra.progresses[index], err = mkblock(mkblk_client, block_size, block)
134+
extra.progresses[index], err, code = mkblock(mkblk_client, block_size, block, host)
121135
if err is not None:
122136
extra.notify_err(index, block_size, err)
123137
return err
@@ -132,8 +146,8 @@ def block_count(size):
132146
return (size + _block_mask) / _block_size
133147

134148

135-
def mkblock(client, block_size, first_chunk):
136-
url = "http://%s/mkblk/%s" % (conf.UP_HOST, block_size)
149+
def mkblock(client, block_size, first_chunk, host):
150+
url = "http://%s/mkblk/%s" % (host, block_size)
137151
content_type = "application/octet-stream"
138152
return client.call_with(url, first_chunk, content_type, len(first_chunk))
139153

@@ -145,8 +159,8 @@ def putblock(client, block_ret, chunk):
145159
return client.call_with(url, chunk, content_type, len(chunk))
146160

147161

148-
def mkfile(client, key, fsize, extra):
149-
url = ["http://%s/mkfile/%s" % (conf.UP_HOST, fsize)]
162+
def mkfile(client, key, fsize, extra, host):
163+
url = ["http://%s/mkfile/%s" % (host, fsize)]
150164

151165
if extra.mimetype:
152166
url.append("mimeType/%s" % urlsafe_b64encode(extra.mimetype))

qiniu/rpc.py

+26-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# -*- coding: utf-8 -*-
2-
import httplib_chunk as httplib
2+
3+
import httplib
4+
5+
if getattr(httplib, "_IMPLEMENTATION", None) != "gae":
6+
# httplib._IMPLEMENTATION is "gae" on GAE
7+
import httplib_chunk as httplib
8+
39
import json
410
import cStringIO
511
import conf
@@ -13,25 +19,32 @@ def __init__(self, host):
1319
self._conn = httplib.HTTPConnection(host)
1420
self._header = {}
1521

16-
def round_tripper(self, method, path, body):
17-
self._conn.request(method, path, body, self._header)
22+
def round_tripper(self, method, path, body, header={}):
23+
header = self.merged_headers(header)
24+
self._conn.request(method, path, body, header)
1825
resp = self._conn.getresponse()
1926
return resp
2027

28+
def merged_headers(self, header):
29+
_header = self._header.copy()
30+
_header.update(header)
31+
return _header
32+
2133
def call(self, path):
22-
return self.call_with(path, None)
34+
ret, err, code = self.call_with(path, None)
35+
return ret, err
2336

2437
def call_with(self, path, body, content_type=None, content_length=None):
2538
ret = None
2639

27-
self.set_header("User-Agent", conf.USER_AGENT)
40+
header = {"User-Agent": conf.USER_AGENT}
2841
if content_type is not None:
29-
self.set_header("Content-Type", content_type)
42+
header["Content-Type"] = content_type
3043

3144
if content_length is not None:
32-
self.set_header("Content-Length", content_length)
45+
header["Content-Length"] = content_length
3346

34-
resp = self.round_tripper("POST", path, body)
47+
resp = self.round_tripper("POST", path, body, header)
3548
try:
3649
ret = resp.read()
3750
ret = json.loads(ret)
@@ -40,16 +53,16 @@ def call_with(self, path, body, content_type=None, content_length=None):
4053
except ValueError:
4154
pass
4255

43-
if resp.status / 100 != 2:
56+
if resp.status >= 400:
4457
err_msg = ret if "error" not in ret else ret["error"]
4558
reqid = resp.getheader("X-Reqid", None)
4659
# detail = resp.getheader("x-log", None)
4760
if reqid is not None:
4861
err_msg += ", reqid:%s" % reqid
4962

50-
return None, err_msg
63+
return None, err_msg, resp.status
5164

52-
return ret, None
65+
return ret, None, resp.status
5366

5467
def call_with_multipart(self, path, fields=None, files=None):
5568
"""
@@ -75,7 +88,8 @@ def call_with_form(self, path, ops):
7588
body = '&'.join(body)
7689

7790
content_type = "application/x-www-form-urlencoded"
78-
return self.call_with(path, body, content_type, len(body))
91+
ret, err, code = self.call_with(path, body, content_type, len(body))
92+
return ret, err
7993

8094
def set_header(self, field, value):
8195
self._header[field] = value

qiniu/rs/rs_token.py

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class PutPolicy(object):
2222
saveKey = None
2323
insertOnly = None
2424
detectMime = None
25+
mimeLimit = None
2526
fsizeLimit = None
2627
persistentNotifyUrl = None
2728
persistentOps = None
@@ -65,6 +66,9 @@ def token(self, mac=None):
6566
if self.detectMime is not None:
6667
token["detectMime"] = self.detectMime
6768

69+
if self.mimeLimit is not None:
70+
token["mimeLimit"] = self.mimeLimit
71+
6872
if self.fsizeLimit is not None:
6973
token["fsizeLimit"] = self.fsizeLimit
7074

qiniu/rsf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def list_prefix(self, bucket, prefix=None, marker=None, limit=None):
3636
if prefix is not None:
3737
ops['prefix'] = prefix
3838
url = '%s?%s' % ('/list', urllib.urlencode(ops))
39-
ret, err = self.conn.call_with(
39+
ret, err, code = self.conn.call_with(
4040
url, body=None, content_type='application/x-www-form-urlencoded')
4141
if ret and not ret.get('marker'):
4242
err = EOF

qiniu/test/io_test.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ def test_put_quote_key():
6363
data = r(100)
6464
key = 'a\\b\\c"你好' + r(9)
6565
ret, err = io.put(policy.token(), key, data)
66-
print err
6766
assert err is None
6867
assert ret['key'].encode('utf8') == key
6968

@@ -114,7 +113,7 @@ def test_put_StringIO():
114113

115114
def test_put_urlopen():
116115
key = "test_%s" % r(9)
117-
data = urllib.urlopen('http://cheneya.qiniudn.com/hello_jpg')
116+
data = urllib.urlopen('http://pythonsdk.qiniudn.com/hello.jpg')
118117
ret, err = io.put(policy.token(), key, data)
119118
assert err is None
120119
assert ret['key'] == key
@@ -178,6 +177,17 @@ def test_put_fail_reqid(self):
178177
ret, err = io.put("", key, data, extra)
179178
assert "reqid" in err
180179

180+
def test_put_with_uphost2(self):
181+
conf.UP_HOST = "api.qiniu.com" # mistake up host
182+
localfile = "%s" % __file__
183+
key = "test_up2_%s" % r(9)
184+
185+
extra.check_crc = 1
186+
ret, err = io.put_file(policy.token(), key, localfile, extra)
187+
assert err is None
188+
assert ret['key'] == key
189+
conf.UP_HOST = "up.qiniu.com"
190+
181191

182192
class Test_get_file_crc32(unittest.TestCase):
183193

0 commit comments

Comments
 (0)