Skip to content

Instantly share code, notes, and snippets.

@mpl
Created January 14, 2016 16:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpl/dc5d285da61e78739e30 to your computer and use it in GitHub Desktop.
Save mpl/dc5d285da61e78739e30 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"encoding/base64"
"flag"
"fmt"
"io"
"log"
"mime"
"net/http"
"regexp"
"strings"
"time"
)
var (
flagHost = flag.String("host", "0.0.0.0:8080", "listening port and hostname")
flagUserpass = flag.String("userpass", "foo:bar", "username:password for basic auth ")
flagHelp = flag.Bool("h", false, "show this help")
)
var (
username string
password string
basicAuthPattern = regexp.MustCompile(`^Basic ([a-zA-Z0-9\+/=]+)`)
)
// basicAuth returns the username and password provided in the Authorization
// header of the request, or an error if anything went wrong.
func basicAuth(req *http.Request) (user string, password string, err error) {
auth := req.Header.Get("Authorization")
if auth == "" {
return "", "", fmt.Errorf("Missing \"Authorization\" in header")
}
matches := basicAuthPattern.FindStringSubmatch(auth)
if len(matches) != 2 {
return "", "", fmt.Errorf("Bogus Authorization header")
}
encoded := matches[1]
enc := base64.StdEncoding
decBuf := make([]byte, enc.DecodedLen(len(encoded)))
n, err := enc.Decode(decBuf, []byte(encoded))
if err != nil {
return "", "", err
}
pieces := strings.SplitN(string(decBuf[0:n]), ":", 2)
if len(pieces) != 2 {
return "", "", fmt.Errorf("didn't get two pieces")
}
return pieces[0], pieces[1], nil
}
func isAllowed(req *http.Request) bool {
user, pass, err := basicAuth(req)
if err != nil {
log.Printf("Authorization failed: %v", err)
return false
}
return user == username && pass == password
}
func sendUnauthorized(rw http.ResponseWriter, req *http.Request) {
realm := ""
rw.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", realm))
rw.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(rw, "<html><body><h1>Unauthorized</h1>")
}
func initUserPass() {
pieces := strings.Split(*flagUserpass, ":")
if len(pieces) < 2 {
log.Fatalf("Wrong userpass auth string: %q; needs to be \"username:password\"", *flagUserpass)
}
username = pieces[0]
password = pieces[1]
}
func receivePost(w http.ResponseWriter, r *http.Request) {
multipart, err := r.MultipartReader()
if err != nil || multipart == nil {
http.Error(w, fmt.Sprintf("multipart reader error: %v", err), http.StatusBadRequest)
return
}
for {
mimePart, err := multipart.NextPart()
if err == io.EOF {
break
}
if err != nil {
log.Printf("Error reading multipart section: %v", err)
http.Error(w, fmt.Sprintf("Error reading multipart section: %v", err), http.StatusInternalServerError)
return
}
contentDisposition, params, err := mime.ParseMediaType(mimePart.Header.Get("Content-Disposition"))
if err != nil {
http.Error(w, "invalid Content-Disposition", http.StatusBadRequest)
return
}
if contentDisposition != "form-data" {
http.Error(w, fmt.Sprintf("Expected Content-Disposition of \"form-data\"; got %q", contentDisposition), http.StatusBadRequest)
return
}
formName := params["name"]
if formName != "someKey" {
http.Error(w, fmt.Sprintf("invalid form name parameter name; got %q, wanted \"someKey\"", formName), http.StatusBadRequest)
return
}
var buf bytes.Buffer
if _, err := io.Copy(&buf, mimePart); err != nil {
log.Printf("error reading form value: %v", err)
http.Error(w, fmt.Sprintf("error reading form value: %v", err), http.StatusInternalServerError)
return
}
formValue := buf.String()
if formValue != "someValue" {
http.Error(w, fmt.Sprintf("invalid form value; got %q, wanted \"someValue\"", formValue), http.StatusBadRequest)
return
}
fmt.Fprintf(w, "%v: Correctly received post value %q in %v to %v", time.Now(), formValue, r.Method, r.URL.Path)
return
}
}
func main() {
flag.Parse()
if *flagHelp {
flag.PrintDefaults()
}
initUserPass()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "NOT A GET", http.StatusBadRequest)
return
}
fmt.Fprint(w, clientCode())
})
http.HandleFunc("/apiget", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "NOT A GET", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "Hello World, it is %v, and I received a %v for %v.", time.Now(), r.Method, r.URL.Path)
})
http.HandleFunc("/apipost", func(w http.ResponseWriter, r *http.Request) {
if isAllowed(r) {
if r.Method != "POST" {
http.Error(w, "NOT A POST", http.StatusBadRequest)
return
}
receivePost(w, r)
return
}
sendUnauthorized(w, r)
})
log.Fatal(http.ListenAndServe(*flagHost, nil))
}
func clientCode() string {
return `
<!doctype html>
<html>
<head>
</head>
<body>
<script>
function reqListener () {
console.log(this.responseText);
}
function get() {
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "/apiget");
oReq.send();
}
function post() {
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
var fd = new FormData();
fd.append("someKey", "someValue");
oReq.open("POST", "/apipost");
oReq.send(fd);
}
function postBlob() {
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
var fd = new FormData();
fd.append("someKey", new Blob(["someValue"]));
oReq.open("POST", "/apipost");
oReq.send(fd);
}
</script>
To see the bug, open the console and START with POST WITH A BLOB.</br>
<button onclick="get()">GET</button>
<button onclick="post()">POST</button>
<button onclick="postBlob()">POST WITH A BLOB</button>
</body>
</html>
`
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment