Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
caddy
Commits
69081360
Commit
69081360
authored
Apr 18, 2016
by
W-Mark Kubacki
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
browse: Split ServeHTTP into small specialized functions
parent
da016f8d
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
110 additions
and
78 deletions
+110
-78
middleware/browse/browse.go
middleware/browse/browse.go
+109
-77
middleware/browse/browse_test.go
middleware/browse/browse_test.go
+1
-1
No files found.
middleware/browse/browse.go
View file @
69081360
...
...
@@ -5,7 +5,6 @@ package browse
import
(
"bytes"
"encoding/json"
"errors"
"net/http"
"net/url"
"os"
...
...
@@ -173,22 +172,20 @@ func (l Listing) applySort() {
}
}
func
directoryListing
(
files
[]
os
.
FileInfo
,
r
*
http
.
Request
,
canGoUp
bool
,
root
string
,
ignoreIndexes
bool
,
vars
interface
{})
(
Listing
,
error
)
{
func
directoryListing
(
files
[]
os
.
FileInfo
,
canGoUp
bool
,
urlPath
string
)
(
Listing
,
bool
)
{
var
(
fileinfos
[]
FileInfo
dirCount
,
fileCount
int
urlPath
=
r
.
URL
.
Path
hasIndexFile
bool
)
for
_
,
f
:=
range
files
{
name
:=
f
.
Name
()
// Directory is not browsable if it contains index file
if
!
ignoreIndexes
{
for
_
,
indexName
:=
range
middleware
.
IndexPages
{
if
name
==
indexName
{
return
Listing
{},
errors
.
New
(
"Directory contains index file, not browsable!"
)
}
for
_
,
indexName
:=
range
middleware
.
IndexPages
{
if
name
==
indexName
{
hasIndexFile
=
true
break
}
}
...
...
@@ -218,16 +215,11 @@ func directoryListing(files []os.FileInfo, r *http.Request, canGoUp bool, root s
Items
:
fileinfos
,
NumDirs
:
dirCount
,
NumFiles
:
fileCount
,
Context
:
middleware
.
Context
{
Root
:
http
.
Dir
(
root
),
Req
:
r
,
URL
:
r
.
URL
,
},
User
:
vars
,
},
nil
},
hasIndexFile
}
// ServeHTTP implements the middleware.Handler interface.
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
// If so, control is handed over to ServeListing.
func
(
b
Browse
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
var
bc
*
Config
// See if there's a browse configuration to match the path
...
...
@@ -274,108 +266,113 @@ inScope:
return
0
,
nil
}
return
b
.
ServeListing
(
w
,
r
,
requestedFilepath
,
bc
)
}
func
(
b
Browse
)
loadDirectoryContents
(
requestedFilepath
,
urlPath
string
)
(
*
Listing
,
bool
,
error
)
{
// Load directory contents
file
,
err
:=
os
.
Open
(
requestedFilepath
)
if
err
!=
nil
{
switch
{
case
os
.
IsPermission
(
err
)
:
return
http
.
StatusForbidden
,
err
case
os
.
IsExist
(
err
)
:
return
http
.
StatusGone
,
err
default
:
return
http
.
StatusInternalServerError
,
err
}
return
nil
,
false
,
err
}
defer
file
.
Close
()
files
,
err
:=
file
.
Readdir
(
-
1
)
if
err
!=
nil
{
switch
{
case
os
.
IsPermission
(
err
)
:
return
http
.
StatusForbidden
,
err
case
os
.
IsExist
(
err
)
:
return
http
.
StatusGone
,
err
default
:
return
http
.
StatusInternalServerError
,
err
}
return
nil
,
false
,
err
}
// Determine if user can browse up another folder
var
canGoUp
bool
curPath
:=
strings
.
TrimSuffix
(
r
.
URL
.
Path
,
"/"
)
curPath
Dir
:=
path
.
Dir
(
strings
.
TrimSuffix
(
urlPath
,
"/"
)
)
for
_
,
other
:=
range
b
.
Configs
{
if
strings
.
HasPrefix
(
path
.
Dir
(
curPath
)
,
other
.
PathScope
)
{
if
strings
.
HasPrefix
(
curPathDir
,
other
.
PathScope
)
{
canGoUp
=
true
break
}
}
// Assemble listing of directory contents
listing
,
err
:=
directoryListing
(
files
,
r
,
canGoUp
,
b
.
Root
,
b
.
IgnoreIndexes
,
bc
.
Variables
)
if
err
!=
nil
{
// directory isn't browsable
return
b
.
Next
.
ServeHTTP
(
w
,
r
)
}
listing
,
hasIndex
:=
directoryListing
(
files
,
canGoUp
,
urlPath
)
// Copy the query values into the Listing struct
listing
.
Sort
,
listing
.
Order
=
r
.
URL
.
Query
()
.
Get
(
"sort"
),
r
.
URL
.
Query
()
.
Get
(
"order"
)
return
&
listing
,
hasIndex
,
nil
}
// handleSortOrder gets and stores for a Listing the 'sort' and 'order'.
//
// This sets Cookies.
func
(
b
Browse
)
handleSortOrder
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
scope
string
)
(
string
,
string
)
{
sort
,
order
:=
r
.
URL
.
Query
()
.
Get
(
"sort"
),
r
.
URL
.
Query
()
.
Get
(
"order"
)
// If the query 'sort' or 'order' is empty, use defaults or any values previously saved in Cookies
if
listing
.
S
ort
==
""
{
listing
.
S
ort
=
"name"
if
s
ort
==
""
{
s
ort
=
"name"
if
sortCookie
,
sortErr
:=
r
.
Cookie
(
"sort"
);
sortErr
==
nil
{
listing
.
S
ort
=
sortCookie
.
Value
s
ort
=
sortCookie
.
Value
}
}
else
{
// Save the query value of 'sort' and 'order' as cookies.
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"sort"
,
Value
:
listing
.
Sort
,
Path
:
bc
.
PathScope
,
Secure
:
r
.
TLS
!=
nil
})
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"order"
,
Value
:
listing
.
Order
,
Path
:
bc
.
PathScope
,
Secure
:
r
.
TLS
!=
nil
})
}
else
{
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"sort"
,
Value
:
sort
,
Path
:
scope
,
Secure
:
r
.
TLS
!=
nil
})
}
if
listing
.
O
rder
==
""
{
listing
.
O
rder
=
"asc"
if
o
rder
==
""
{
o
rder
=
"asc"
if
orderCookie
,
orderErr
:=
r
.
Cookie
(
"order"
);
orderErr
==
nil
{
listing
.
O
rder
=
orderCookie
.
Value
o
rder
=
orderCookie
.
Value
}
}
else
{
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"order"
,
Value
:
listing
.
Order
,
Path
:
bc
.
PathS
cope
,
Secure
:
r
.
TLS
!=
nil
})
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"order"
,
Value
:
order
,
Path
:
s
cope
,
Secure
:
r
.
TLS
!=
nil
})
}
return
sort
,
order
}
// ServeListing returns a formatted view of 'requestedFilepath' contents'.
func
(
b
Browse
)
ServeListing
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
requestedFilepath
string
,
bc
*
Config
)
(
int
,
error
)
{
listing
,
containsIndex
,
err
:=
b
.
loadDirectoryContents
(
requestedFilepath
,
r
.
URL
.
Path
)
if
err
!=
nil
{
switch
{
case
os
.
IsPermission
(
err
)
:
return
http
.
StatusForbidden
,
err
case
os
.
IsExist
(
err
)
:
return
http
.
StatusGone
,
err
default
:
return
http
.
StatusInternalServerError
,
err
}
}
if
containsIndex
&&
!
b
.
IgnoreIndexes
{
// directory isn't browsable
return
b
.
Next
.
ServeHTTP
(
w
,
r
)
}
listing
.
Context
=
middleware
.
Context
{
Root
:
http
.
Dir
(
b
.
Root
),
Req
:
r
,
URL
:
r
.
URL
,
}
listing
.
User
=
bc
.
Variables
// Copy the query values into the Listing struct
listing
.
Sort
,
listing
.
Order
=
b
.
handleSortOrder
(
w
,
r
,
bc
.
PathScope
)
listing
.
applySort
()
var
buf
bytes
.
Buffer
// Check if we should provide json
acceptHeader
:=
strings
.
Join
(
r
.
Header
[
"Accept"
],
","
)
if
strings
.
Contains
(
strings
.
ToLower
(
acceptHeader
),
"application/json"
)
{
var
marsh
[]
byte
// Check if we are limited
var
buf
*
bytes
.
Buffer
acceptHeader
:=
strings
.
ToLower
(
strings
.
Join
(
r
.
Header
[
"Accept"
],
","
))
switch
{
case
strings
.
Contains
(
acceptHeader
,
"application/json"
)
:
var
limit
int
if
limitQuery
:=
r
.
URL
.
Query
()
.
Get
(
"limit"
);
limitQuery
!=
""
{
limit
,
err
:
=
strconv
.
Atoi
(
limitQuery
)
limit
,
err
=
strconv
.
Atoi
(
limitQuery
)
if
err
!=
nil
{
// if the 'limit' query can't be interpreted as a number, return err
return
http
.
StatusBadRequest
,
err
}
// if `limit` is equal or less than len(listing.Items) and bigger than 0, list them
if
limit
<=
len
(
listing
.
Items
)
&&
limit
>
0
{
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
[
:
limit
])
}
else
{
// if the 'limit' query is empty, or has the wrong value, list everything
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
)
}
if
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
}
}
else
{
// There's no 'limit' query; list them all
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
)
if
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
}
}
// Write the marshaled json to buf
if
_
,
err
=
buf
.
Write
(
marsh
);
err
!=
nil
{
if
buf
,
err
=
b
.
formatAsJSON
(
listing
,
bc
,
limit
);
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
}
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/json; charset=utf-8"
)
}
else
{
// There's no 'application/json' in the 'Accept' header; browse normally
err
=
bc
.
Template
.
Execute
(
&
buf
,
listing
)
if
err
!=
nil
{
default
:
// There's no 'application/json' in the 'Accept' header; browse normally
if
buf
,
err
=
b
.
formatAsHTML
(
listing
,
bc
);
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
}
w
.
Header
()
.
Set
(
"Content-Type"
,
"text/html; charset=utf-8"
)
...
...
@@ -386,3 +383,38 @@ inScope:
return
http
.
StatusOK
,
nil
}
func
(
b
Browse
)
formatAsJSON
(
listing
*
Listing
,
bc
*
Config
,
limit
int
)
(
*
bytes
.
Buffer
,
error
)
{
buf
:=
new
(
bytes
.
Buffer
)
var
marsh
[]
byte
var
err
error
// Check if we are limited
if
limit
>
0
{
// if `limit` is equal or less than len(listing.Items) and bigger than 0, list them
if
limit
<=
len
(
listing
.
Items
)
{
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
[
:
limit
])
}
else
{
// if the 'limit' query is empty, or has the wrong value, list everything
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
)
}
if
err
!=
nil
{
return
nil
,
err
}
}
else
{
// There's no 'limit' query; list them all
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
)
if
err
!=
nil
{
return
nil
,
err
}
}
// Write the marshaled json to buf
_
,
err
=
buf
.
Write
(
marsh
)
return
buf
,
err
}
func
(
b
Browse
)
formatAsHTML
(
listing
*
Listing
,
bc
*
Config
)
(
*
bytes
.
Buffer
,
error
)
{
buf
:=
new
(
bytes
.
Buffer
)
err
:=
bc
.
Template
.
Execute
(
buf
,
listing
)
return
buf
,
err
}
middleware/browse/browse_test.go
View file @
69081360
...
...
@@ -315,7 +315,7 @@ func TestBrowseJson(t *testing.T) {
code
,
err
:=
b
.
ServeHTTP
(
rec
,
req
)
if
code
!=
http
.
StatusOK
{
t
.
Fatalf
(
"
Wrong status, expected %d, got %d"
,
http
.
StatusOK
,
code
)
t
.
Fatalf
(
"
In test %d: Wrong status, expected %d, got %d"
,
i
,
http
.
StatusOK
,
code
)
}
if
rec
.
HeaderMap
.
Get
(
"Content-Type"
)
!=
"application/json; charset=utf-8"
{
t
.
Fatalf
(
"Expected Content type to be application/json; charset=utf-8, but got %s "
,
rec
.
HeaderMap
.
Get
(
"Content-Type"
))
...
...
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