This commit is contained in:
Maxime 2015-09-02 15:16:06 +02:00
commit bdcbd11d65
13 changed files with 266 additions and 17 deletions

2
.gitignore vendored
View File

@ -12,3 +12,5 @@ access.log
/*.conf /*.conf
Caddyfile Caddyfile
og_static/

View File

@ -43,6 +43,7 @@ By default, Caddy serves the current directory at [localhost:2015](http://localh
Caddy accepts some flags from the command line. Run `caddy -h` to view the help for flags. You can also pipe a Caddyfile into the caddy command. Caddy accepts some flags from the command line. Run `caddy -h` to view the help for flags. You can also pipe a Caddyfile into the caddy command.
**Running as root:** We advise against this; use setcap instead, like so: `setcap cap_net_bind_service=+ep ./caddy` This will allow you to listen on ports below 1024 (like 80 and 443).
#### Docker Container #### Docker Container
@ -51,6 +52,7 @@ Caddy is available as a Docker container from any of these sources:
- [abiosoft/caddy](https://registry.hub.docker.com/u/abiosoft/caddy/) - [abiosoft/caddy](https://registry.hub.docker.com/u/abiosoft/caddy/)
- [darron/caddy](https://registry.hub.docker.com/u/darron/caddy/) - [darron/caddy](https://registry.hub.docker.com/u/darron/caddy/)
- [joshix/caddy](https://registry.hub.docker.com/u/joshix/caddy/)
- [jumanjiman/caddy](https://registry.hub.docker.com/u/jumanjiman/caddy/) - [jumanjiman/caddy](https://registry.hub.docker.com/u/jumanjiman/caddy/)
- [zenithar/nano-caddy](https://registry.hub.docker.com/u/zenithar/nano-caddy/) - [zenithar/nano-caddy](https://registry.hub.docker.com/u/zenithar/nano-caddy/)

View File

@ -20,7 +20,7 @@ const (
Name = "Caddy" Name = "Caddy"
// Version is the program version // Version is the program version
Version = "0.7.4" Version = "0.7.5"
) )
var ( var (

View File

@ -57,6 +57,11 @@ func Load(filename string, input io.Reader) (Group, error) {
if config.Port == "" { if config.Port == "" {
config.Port = Port config.Port = Port
} }
if config.Port == "http" {
config.TLS.Enabled = false
log.Printf("Warning: TLS disabled for %s://%s. To force TLS over the plaintext HTTP port, "+
"specify port 80 explicitly (https://%s:80).", config.Port, config.Host, config.Host)
}
if i == 0 { if i == 0 {
sharedConfig.Startup = []func() error{} sharedConfig.Startup = []func() error{}
sharedConfig.Shutdown = []func() error{} sharedConfig.Shutdown = []func() error{}

View File

@ -31,7 +31,7 @@ func FastCGI(c *Controller) (middleware.Middleware, error) {
SoftwareName: c.AppName, SoftwareName: c.AppName,
SoftwareVersion: c.AppVersion, SoftwareVersion: c.AppVersion,
ServerName: c.Host, ServerName: c.Host,
ServerPort: c.Port, ServerPort: c.Port, // BUG: This is not known until the server blocks are split up...
} }
}, nil }, nil
} }

View File

@ -2,7 +2,6 @@ package setup
import ( import (
"crypto/tls" "crypto/tls"
"log"
"strings" "strings"
"github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware"
@ -10,11 +9,6 @@ import (
func TLS(c *Controller) (middleware.Middleware, error) { func TLS(c *Controller) (middleware.Middleware, error) {
c.TLS.Enabled = true c.TLS.Enabled = true
if c.Port == "http" {
c.TLS.Enabled = false
log.Printf("Warning: TLS disabled for %s://%s. To force TLS over the plaintext HTTP port, "+
"specify port 80 explicitly (https://%s:80).", c.Port, c.Host, c.Host)
}
for c.Next() { for c.Next() {
if !c.NextArg() { if !c.NextArg() {

7
dist/CHANGES.txt vendored
View File

@ -1,9 +1,14 @@
CHANGES CHANGES
<master> 0.7.5 (August 5, 2015)
- core: All listeners bind to 0.0.0.0 unless 'bind' directive is used
- fastcgi: Set HTTPS env variable if connection is secure - fastcgi: Set HTTPS env variable if connection is secure
- log: Output to system log (except Windows)
- markdown: Added dev command to disable caching during development
- markdown: Fixed error reporting during initial site generation - markdown: Fixed error reporting during initial site generation
- markdown: Fixed crash if path does not exist when server starts - markdown: Fixed crash if path does not exist when server starts
- markdown: Fixed site generation and link indexing when files change
- templates: Added .NowDate for use in date-related functions
- Several bug fixes related to startup and shutdown functions - Several bug fixes related to startup and shutdown functions

2
dist/README.txt vendored
View File

@ -1,4 +1,4 @@
CADDY 0.7.4 CADDY 0.7.5
Website Website
https://caddyserver.com https://caddyserver.com

View File

@ -4,12 +4,14 @@ package browse
import ( import (
"bytes" "bytes"
"encoding/json"
"errors" "errors"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path" "path"
"sort" "sort"
"strconv"
"strings" "strings"
"text/template" "text/template"
"time" "time"
@ -185,7 +187,6 @@ func directoryListing(files []os.FileInfo, r *http.Request, canGoUp bool, root s
// ServeHTTP implements the middleware.Handler interface. // ServeHTTP implements the middleware.Handler interface.
func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
filename := b.Root + r.URL.Path filename := b.Root + r.URL.Path
info, err := os.Stat(filename) info, err := os.Stat(filename)
if err != nil { if err != nil {
return b.Next.ServeHTTP(w, r) return b.Next.ServeHTTP(w, r)
@ -264,12 +265,47 @@ func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
listing.applySort() listing.applySort()
var buf bytes.Buffer var buf bytes.Buffer
err = bc.Template.Execute(&buf, listing) // check if we should provide json
if err != nil { acceptHeader := strings.Join(r.Header["Accept"], ",")
return http.StatusInternalServerError, err if strings.Contains(strings.ToLower(acceptHeader), "application/json") {
var marsh []byte
// check if we are limited
if limitQuery := r.URL.Query().Get("limit"); 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 {
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 {
return http.StatusInternalServerError, err
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
} }
w.Header().Set("Content-Type", "text/html; charset=utf-8")
buf.WriteTo(w) buf.WriteTo(w)
return http.StatusOK, nil return http.StatusOK, nil

View File

@ -1,14 +1,16 @@
package browse package browse
import ( import (
"encoding/json"
"github.com/mholt/caddy/middleware"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url"
"os"
"sort" "sort"
"testing" "testing"
"text/template" "text/template"
"time" "time"
"github.com/mholt/caddy/middleware"
) )
// "sort" package has "IsSorted" function, but no "IsReversed"; // "sort" package has "IsSorted" function, but no "IsReversed";
@ -154,4 +156,88 @@ func TestBrowseTemplate(t *testing.T) {
if respBody != expectedBody { if respBody != expectedBody {
t.Fatalf("Expected body: %v got: %v", expectedBody, respBody) t.Fatalf("Expected body: %v got: %v", expectedBody, respBody)
} }
}
func TestBrowseJson(t *testing.T) {
b := Browse{
Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
t.Fatalf("Next shouldn't be called")
return 0, nil
}),
Root: "./testdata",
Configs: []Config{
Config{
PathScope: "/photos",
},
},
}
req, err := http.NewRequest("GET", "/photos/", nil)
if err != nil {
t.Fatalf("Test: Could not create HTTP request: %v", err)
}
req.Header.Set("Accept", "application/json")
rec := httptest.NewRecorder()
b.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Wrong status, expected %d, got %d", http.StatusOK, rec.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"))
}
actualJsonResponseString := rec.Body.String()
//generating the listing to compare it with the response body
file, err := os.Open(b.Root + req.URL.Path)
if err != nil {
if os.IsPermission(err) {
t.Fatalf("Os Permission Error")
}
}
defer file.Close()
files, err := file.Readdir(-1)
if err != nil {
t.Fatalf("Unable to Read Contents of the directory")
}
var fileinfos []FileInfo
for _, f := range files {
name := f.Name()
if f.IsDir() {
name += "/"
}
url := url.URL{Path: name}
fileinfos = append(fileinfos, FileInfo{
IsDir: f.IsDir(),
Name: f.Name(),
Size: f.Size(),
URL: url.String(),
ModTime: f.ModTime(),
Mode: f.Mode(),
})
}
listing := Listing{
Items: fileinfos,
}
listing.Sort = "name"
listing.Order = "asc"
listing.applySort()
marsh, err := json.Marshal(listing.Items)
if err != nil {
t.Fatalf("Unable to Marshal the listing ")
}
expectedJsonString := string(marsh)
if actualJsonResponseString != expectedJsonString {
t.Errorf("Json response string doesnt match the expected Json response ")
}
} }

View File

@ -0,0 +1,41 @@
package middleware
import (
"net/http"
"testing"
)
func TestIndexfile(t *testing.T) {
tests := []struct {
rootDir http.FileSystem
fpath string
indexFiles []string
shouldErr bool
expectedFilePath string //retun value
expectedBoolValue bool //return value
}{
{
http.Dir("./templates/testdata"), "/images/", []string{"img.htm"},
false,
"/images/img.htm", true,
},
}
for i, test := range tests {
actualFilePath, actualBoolValue := IndexFile(test.rootDir, test.fpath, test.indexFiles)
if actualBoolValue == true && test.shouldErr {
t.Errorf("Test %d didn't error, but it should have", i)
} else if actualBoolValue != true && !test.shouldErr {
t.Errorf("Test %d errored, but it shouldn't have; got %s", i, "Please Add a / at the end of fpath or the indexFiles doesnt exist")
}
if actualFilePath != test.expectedFilePath {
t.Fatalf("Test %d expected returned filepath to be %s, but got %s ",
i, test.expectedFilePath, actualFilePath)
}
if actualBoolValue != test.expectedBoolValue {
t.Fatalf("Test %d expected returned bool value to be %v, but got %v ",
i, test.expectedBoolValue, actualBoolValue)
}
}
}

View File

@ -0,0 +1,40 @@
package middleware
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestNewResponseRecorder(t *testing.T) {
w := httptest.NewRecorder()
recordRequest := NewResponseRecorder(w)
if !(recordRequest.ResponseWriter == w) {
t.Fatalf("Expected Response writer in the Recording to be same as the one sent\n")
}
if recordRequest.status != http.StatusOK {
t.Fatalf("Expected recorded status to be http.StatusOK (%d) , but found %d\n ", recordRequest.status)
}
}
func TestWriteHeader(t *testing.T) {
w := httptest.NewRecorder()
recordRequest := NewResponseRecorder(w)
recordRequest.WriteHeader(401)
if w.Code != 401 || recordRequest.status != 401 {
t.Fatalf("Expected Response status to be set to 401, but found %d\n", recordRequest.status)
}
}
func TestWrite(t *testing.T) {
w := httptest.NewRecorder()
responseTestString := "test"
recordRequest := NewResponseRecorder(w)
buf := []byte(responseTestString)
recordRequest.Write(buf)
if recordRequest.size != len(buf) {
t.Fatalf("Expected the bytes written counter to be %d, but instead found %d\n", len(buf), recordRequest.size)
}
if w.Body.String() != responseTestString {
t.Fatalf("Expected Response Body to be %s , but found %s\n", w.Body.String())
}
}

View File

@ -0,0 +1,38 @@
package middleware
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestNewReplacer(t *testing.T) {
w := httptest.NewRecorder()
recordRequest := NewResponseRecorder(w)
userJson := `{"username": "dennis"}`
reader := strings.NewReader(userJson) //Convert string to reader
request, err := http.NewRequest("POST", "http://caddyserver.com", reader) //Create request with JSON body
if err != nil {
t.Fatalf("Request Formation Failed \n")
}
replaceValues := NewReplacer(request, recordRequest, "")
switch v := replaceValues.(type) {
case replacer:
if v.replacements["{host}"] != "caddyserver.com" {
t.Errorf("Expected host to be caddyserver.com")
}
if v.replacements["{method}"] != "POST" {
t.Errorf("Expected request method to be POST")
}
if v.replacements["{status}"] != "200" {
t.Errorf("Expected status to be 200")
}
default:
t.Fatalf("Return Value from New Replacer expected pass type assertion into a replacer type \n")
}
}