Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pyfs
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
pyfs
Commits
30599d4b
Commit
30599d4b
authored
May 14, 2011
by
rfkelly0
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
DAVFS: re-use persistent connections when possible
parent
5686efd7
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
85 additions
and
21 deletions
+85
-21
fs/contrib/davfs/__init__.py
+83
-19
fs/contrib/davfs/xmlobj.py
+2
-2
No files found.
fs/contrib/davfs/__init__.py
View file @
30599d4b
...
@@ -16,6 +16,8 @@ Requires the dexml module:
...
@@ -16,6 +16,8 @@ Requires the dexml module:
# Copyright (c) 2009-2010, Cloud Matrix Pty. Ltd.
# Copyright (c) 2009-2010, Cloud Matrix Pty. Ltd.
# All rights reserved; available under the terms of the MIT License.
# All rights reserved; available under the terms of the MIT License.
from
__future__
import
with_statement
import
os
import
os
import
sys
import
sys
import
httplib
import
httplib
...
@@ -26,10 +28,13 @@ from urllib import quote as urlquote
...
@@ -26,10 +28,13 @@ from urllib import quote as urlquote
from
urllib
import
unquote
as
urlunquote
from
urllib
import
unquote
as
urlunquote
import
base64
import
base64
import
re
import
re
import
time
import
datetime
import
datetime
import
cookielib
import
cookielib
import
fnmatch
import
fnmatch
import
xml.dom.pulldom
import
xml.dom.pulldom
import
threading
from
collections
import
deque
import
fs
import
fs
from
fs.base
import
*
from
fs.base
import
*
...
@@ -73,6 +78,11 @@ class DAVFS(FS):
...
@@ -73,6 +78,11 @@ class DAVFS(FS):
"https"
:
httplib
.
HTTPSConnection
,
"https"
:
httplib
.
HTTPSConnection
,
}
}
_DEFAULT_PORT_NUMBERS
=
{
"http"
:
80
,
"https"
:
443
,
}
_meta
=
{
'virtual'
:
False
,
_meta
=
{
'virtual'
:
False
,
'read_only'
:
False
,
'read_only'
:
False
,
'unicode_paths'
:
True
,
'unicode_paths'
:
True
,
...
@@ -100,6 +110,8 @@ class DAVFS(FS):
...
@@ -100,6 +110,8 @@ class DAVFS(FS):
self
.
connection_classes
=
self
.
connection_classes
.
copy
()
self
.
connection_classes
=
self
.
connection_classes
.
copy
()
self
.
connection_classes
.
update
(
connection_classes
)
self
.
connection_classes
.
update
(
connection_classes
)
self
.
_connections
=
[]
self
.
_connections
=
[]
self
.
_free_connections
=
{}
self
.
_connection_lock
=
threading
.
Lock
()
self
.
_cookiejar
=
cookielib
.
CookieJar
()
self
.
_cookiejar
=
cookielib
.
CookieJar
()
super
(
DAVFS
,
self
)
.
__init__
(
thread_synchronize
=
thread_synchronize
)
super
(
DAVFS
,
self
)
.
__init__
(
thread_synchronize
=
thread_synchronize
)
# Check that the server speaks WebDAV, and normalize the URL
# Check that the server speaks WebDAV, and normalize the URL
...
@@ -125,24 +137,75 @@ class DAVFS(FS):
...
@@ -125,24 +137,75 @@ class DAVFS(FS):
con
.
close
()
con
.
close
()
super
(
DAVFS
,
self
)
.
close
()
super
(
DAVFS
,
self
)
.
close
()
def
_add_connection
(
self
,
con
):
def
_take_connection
(
self
,
url
):
self
.
_connections
.
append
(
con
)
"""Get a connection to the given url's host, re-using if possible."""
scheme
=
url
.
scheme
.
lower
()
def
_del_connection
(
self
,
con
):
hostname
=
url
.
hostname
port
=
url
.
port
if
not
port
:
try
:
try
:
self
.
_connections
.
remove
(
con
)
port
=
self
.
_DEFAULT_PORT_NUMBERS
[
scheme
]
except
ValueError
:
except
KeyError
:
pass
msg
=
"unsupported protocol: '
%
s'"
%
(
url
.
scheme
,)
raise
RemoteConnectionError
(
msg
=
msg
)
# Can we re-use an existing connection?
with
self
.
_connection_lock
:
now
=
time
.
time
()
try
:
free_connections
=
self
.
_free_connections
[(
hostname
,
port
)]
except
KeyError
:
self
.
_free_connections
[(
hostname
,
port
)]
=
deque
()
free_connections
=
self
.
_free_connections
[(
hostname
,
port
)]
else
:
else
:
while
free_connections
:
(
when
,
con
)
=
free_connections
.
popleft
()
if
when
+
30
>
now
:
return
(
False
,
con
)
self
.
_discard_connection
(
con
)
# Nope, we need to make a fresh one.
try
:
ConClass
=
self
.
connection_classes
[
scheme
]
except
KeyError
:
msg
=
"unsupported protocol: '
%
s'"
%
(
url
.
scheme
,)
raise
RemoteConnectionError
(
msg
=
msg
)
con
=
ConClass
(
url
.
hostname
,
url
.
port
,
timeout
=
self
.
timeout
)
self
.
_connections
.
append
(
con
)
return
(
True
,
con
)
def
_give_connection
(
self
,
url
,
con
):
"""Return a connection to the pool, or destroy it if dead."""
scheme
=
url
.
scheme
.
lower
()
hostname
=
url
.
hostname
port
=
url
.
port
if
not
port
:
try
:
port
=
self
.
_DEFAULT_PORT_NUMBERS
[
scheme
]
except
KeyError
:
msg
=
"unsupported protocol: '
%
s'"
%
(
url
.
scheme
,)
raise
RemoteConnectionError
(
msg
=
msg
)
with
self
.
_connection_lock
:
now
=
time
.
time
()
try
:
free_connections
=
self
.
_free_connections
[(
hostname
,
port
)]
except
KeyError
:
self
.
_free_connections
[(
hostname
,
port
)]
=
deque
()
free_connections
=
self
.
_free_connections
[(
hostname
,
port
)]
free_connections
.
append
((
now
,
con
))
def
_discard_connection
(
self
,
con
):
con
.
close
()
con
.
close
()
self
.
_connections
.
remove
(
con
)
def
__str__
(
self
):
def
__str__
(
self
):
return
'<DAVFS:
%
s>'
%
(
self
.
url
,)
return
'<DAVFS:
%
s>'
%
(
self
.
url
,)
__repr__
=
__str__
__repr__
=
__str__
def
__getstate__
(
self
):
def
__getstate__
(
self
):
# Python2.5 cannot load pickled urlparse.ParseResult objects.
state
=
super
(
DAVFS
,
self
)
.
__getstate__
()
state
=
super
(
DAVFS
,
self
)
.
__getstate__
()
del
state
[
"_connection_lock"
]
del
state
[
"_connections"
]
del
state
[
"_free_connections"
]
# Python2.5 cannot load pickled urlparse.ParseResult objects.
del
state
[
"_url_p"
]
del
state
[
"_url_p"
]
# CookieJar objects contain a lock, so they can't be pickled.
# CookieJar objects contain a lock, so they can't be pickled.
del
state
[
"_cookiejar"
]
del
state
[
"_cookiejar"
]
...
@@ -150,6 +213,9 @@ class DAVFS(FS):
...
@@ -150,6 +213,9 @@ class DAVFS(FS):
def
__setstate__
(
self
,
state
):
def
__setstate__
(
self
,
state
):
super
(
DAVFS
,
self
)
.
__setstate__
(
state
)
super
(
DAVFS
,
self
)
.
__setstate__
(
state
)
self
.
_connections
=
[]
self
.
_free_connections
=
{}
self
.
_connection_lock
=
threading
.
Lock
()
self
.
_url_p
=
urlparse
(
self
.
url
)
self
.
_url_p
=
urlparse
(
self
.
url
)
self
.
_cookiejar
=
cookielib
.
CookieJar
()
self
.
_cookiejar
=
cookielib
.
CookieJar
()
...
@@ -165,7 +231,7 @@ class DAVFS(FS):
...
@@ -165,7 +231,7 @@ class DAVFS(FS):
def
_url2path
(
self
,
url
):
def
_url2path
(
self
,
url
):
"""Convert a server-side URL into a client-side path."""
"""Convert a server-side URL into a client-side path."""
path
=
urlunquote
(
urlparse
(
url
)
.
path
)
path
=
urlunquote
(
urlparse
(
url
)
.
path
)
root
=
self
.
_url_p
.
path
root
=
urlunquote
(
self
.
_url_p
.
path
)
path
=
path
[
len
(
root
)
-
1
:]
.
decode
(
"utf8"
)
path
=
path
[
len
(
root
)
-
1
:]
.
decode
(
"utf8"
)
while
path
.
endswith
(
"/"
):
while
path
.
endswith
(
"/"
):
path
=
path
[:
-
1
]
path
=
path
[:
-
1
]
...
@@ -232,13 +298,7 @@ class DAVFS(FS):
...
@@ -232,13 +298,7 @@ class DAVFS(FS):
headers
[
"Authorization"
]
=
creds
headers
[
"Authorization"
]
=
creds
(
size
,
chunks
)
=
normalize_req_body
(
body
)
(
size
,
chunks
)
=
normalize_req_body
(
body
)
try
:
try
:
try
:
(
fresh
,
con
)
=
self
.
_take_connection
(
url
)
ConClass
=
self
.
connection_classes
[
url
.
scheme
.
lower
()]
except
KeyError
:
msg
=
"unsupported protocol: '
%
s'"
%
(
url
.
scheme
,)
raise
RemoteConnectionError
(
msg
=
msg
)
con
=
ConClass
(
url
.
hostname
,
url
.
port
,
timeout
=
self
.
timeout
)
self
.
_add_connection
(
con
)
try
:
try
:
con
.
putrequest
(
method
,
url
.
path
)
con
.
putrequest
(
method
,
url
.
path
)
if
size
is
not
None
:
if
size
is
not
None
:
...
@@ -256,17 +316,21 @@ class DAVFS(FS):
...
@@ -256,17 +316,21 @@ class DAVFS(FS):
raise
RemoteConnectionError
(
""
,
msg
=
"FS is closed"
)
raise
RemoteConnectionError
(
""
,
msg
=
"FS is closed"
)
resp
=
con
.
getresponse
()
resp
=
con
.
getresponse
()
self
.
_cookiejar
.
extract_cookies
(
FakeResp
(
resp
),
FakeReq
(
con
,
url
.
scheme
,
url
.
path
))
self
.
_cookiejar
.
extract_cookies
(
FakeResp
(
resp
),
FakeReq
(
con
,
url
.
scheme
,
url
.
path
))
except
Exception
,
e
:
except
Exception
:
self
.
_d
el
_connection
(
con
)
self
.
_d
iscard
_connection
(
con
)
raise
raise
else
:
else
:
old_close
=
resp
.
close
old_close
=
resp
.
close
def
new_close
():
def
new_close
():
del
resp
.
close
old_close
()
old_close
()
self
.
_del_connection
(
con
)
con
.
close
()
self
.
_give_connection
(
url
,
con
)
resp
.
close
=
new_close
resp
.
close
=
new_close
return
resp
return
resp
except
socket
.
error
,
e
:
except
socket
.
error
,
e
:
if
not
fresh
:
return
self
.
_raw_request
(
url
,
method
,
body
,
headers
,
num_tries
)
if
e
.
args
[
0
]
in
_RETRYABLE_ERRORS
:
if
e
.
args
[
0
]
in
_RETRYABLE_ERRORS
:
if
num_tries
<
3
:
if
num_tries
<
3
:
num_tries
+=
1
num_tries
+=
1
...
...
fs/contrib/davfs/xmlobj.py
View file @
30599d4b
...
@@ -134,9 +134,9 @@ class response(_davbase):
...
@@ -134,9 +134,9 @@ class response(_davbase):
"""XML model for an individual response in a multi-status message."""
"""XML model for an individual response in a multi-status message."""
href
=
HrefField
()
href
=
HrefField
()
# TODO: ensure only one of hrefs/propstats
# TODO: ensure only one of hrefs/propstats
hrefs
=
fields
.
List
(
HrefField
(),
minlength
=
1
,
required
=
False
)
hrefs
=
fields
.
List
(
HrefField
(),
required
=
False
)
status
=
StatusField
(
required
=
False
)
status
=
StatusField
(
required
=
False
)
propstats
=
fields
.
List
(
"propstat"
,
minlenth
=
1
,
required
=
False
)
propstats
=
fields
.
List
(
"propstat"
,
required
=
False
)
description
=
fields
.
String
(
tagname
=
"responsedescription"
,
required
=
False
)
description
=
fields
.
String
(
tagname
=
"responsedescription"
,
required
=
False
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment