From bb9497361b566884a1e4cb6d76a6f539383bd32c Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Sat, 23 Jul 2016 18:34:39 +0200 Subject: greatly simplified web uploads diff --git a/src/rhimportd/routeWeb.go b/src/rhimportd/routeWeb.go index 22db1d7..d0582a2 100644 --- a/src/rhimportd/routeWeb.go +++ b/src/rhimportd/routeWeb.go @@ -50,7 +50,7 @@ func StartWebRouter(addr, staticDir string, conf *rhimport.Config, db *rddb.DBCh // http.Handle("/trusted/simple", webHandler{conf, db, sessions, true, webSimpleHandler}) http.Handle("/public/simple", webHandler{conf, db, sessions, false, webSimpleHandler}) http.Handle("/public/socket", webHandler{conf, db, sessions, false, webSocketHandler}) - http.Handle("/public/upload", webHandler{conf, db, sessions, false, webUploadHandler}) + http.Handle("/public/upload/", http.StripPrefix("/public/upload/", webHandler{conf, db, sessions, false, webUploadHandler})) rhl.Println("web-router: listening on", addr) server := &http.Server{Addr: addr, ReadTimeout: 2 * time.Hour, WriteTimeout: 2 * time.Hour} diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go index 84605e6..9d16e92 100644 --- a/src/rhimportd/uploadWeb.go +++ b/src/rhimportd/uploadWeb.go @@ -25,11 +25,10 @@ package main import ( - "bytes" "encoding/json" "io" - "mime/multipart" "net/http" + "strings" "code.helsinki.at/rhrd-go/rddb" "code.helsinki.at/rhrd-go/rhimport" @@ -56,107 +55,45 @@ func webUploadSuccessResponse(w http.ResponseWriter) { encoder.Encode(respdata) } -const ( - webUploadMaxRequestSize = (2 << 30) - 1 // 2GB, (2 << 30) overflows int on 32-bit systems therefore we use 2GB - 1 Byte -) - -func webUploadParseForm(r *http.Request) (username, sessionid, srcfile string, src *multipart.Part, err error) { - var mpr *multipart.Reader - if mpr, err = r.MultipartReader(); err != nil { - return - } - - for { - var p *multipart.Part - if p, err = mpr.NextPart(); err != nil { - if err == io.EOF { - err = nil - } - return - } - var dstfield *string - switch p.FormName() { - case "LOGIN_NAME": - dstfield = &username - case "SESSION_ID": - dstfield = &sessionid - case "FILENAME": - srcfile = p.FileName() - src = p - return // don't read any fields beyond this point because we would need to load the whole file in order to continue parsing - default: - rhdl.Printf("WebUploadHandler: ingoring unknown form field: '%s'", p.FormName()) - continue - } - - var buf bytes.Buffer - if _, err = io.CopyN(&buf, p, 1<<10); err != nil && err != io.EOF { // 1kB should be enough - return - } - *dstfield = buf.String() - p.Close() - } -} - func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan, trusted bool, w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" { - http.Redirect(w, r, "/static/upload.html", http.StatusTemporaryRedirect) - return - } + rhdl.Printf("WebUploadHandler: got request for %s", r.URL.String()) if r.Method != "POST" { rhl.Printf("WebUploadHandler: got invalid request method") - webUploadErrorResponse(w, http.StatusMethodNotAllowed, "only POST and GET method are allowed") - return - } - - // This is from: stackoverflow.com/questions/26392196 - if r.ContentLength > webUploadMaxRequestSize { - rhl.Printf("WebUploadHandler: request is too large: %d > %d", r.ContentLength, webUploadMaxRequestSize) - webUploadErrorResponse(w, http.StatusExpectationFailed, "request too large") + webUploadErrorResponse(w, http.StatusMethodNotAllowed, "only POST method is allowed") return } - r.Body = http.MaxBytesReader(w, r.Body, webUploadMaxRequestSize) - username, sessionid, srcfile, src, err := webUploadParseForm(r) - if err != nil { - rhl.Printf("WebUploadHandler: error while parsing multipart-form: %v", err) - webUploadErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - if username == "" { - webUploadErrorResponse(w, http.StatusBadRequest, "missing field LOGIN_NAME") - return - } - if sessionid == "" { - webUploadErrorResponse(w, http.StatusBadRequest, "missing field SESSION_ID") - return - } - if srcfile == "" || src == nil { - webUploadErrorResponse(w, http.StatusBadRequest, "missing field FILENAME") + parts := strings.SplitN(r.URL.String(), "/", 2) + if len(parts) != 2 { + rhl.Printf("WebUploadHandler: got invalid request url") + webUploadErrorResponse(w, http.StatusBadRequest, "invalid request URL, should be /") return } + username := parts[0] + sessionid := parts[1] s, _, code, _ := sessions.Get(username, sessionid) if code != http.StatusOK { + rhl.Printf("WebUploadHandler: refusing file upload for '%s/%s': session not found", username, sessionid) webUploadErrorResponse(w, http.StatusUnauthorized, "session not found") return } cancel, attachmentChan := s.AttachUploader() if attachmentChan == nil || cancel == nil { - rhl.Printf("WebUploadHandler: refusing file upload for '%s': session already uploading", srcfile) + rhl.Printf("WebUploadHandler: refusing file upload for '%s/%s': session already uploading", username, sessionid) webUploadErrorResponse(w, http.StatusForbidden, "upload already in progress") return } defer close(attachmentChan) - rhl.Printf("WebUploadHandler: starting upload for file '%s'", srcfile) + rhl.Printf("WebUploadHandler: starting file upload for '%s/%s'", username, sessionid) for { chunk := rhimport.AttachmentChunk{} var data [128 * 1024]byte - n, err := src.Read(data[:]) + n, err := r.Body.Read(data[:]) if n > 0 { chunk.Data = data[:n] if err == io.EOF { @@ -165,14 +102,14 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport } chunk.Error = err if err == io.EOF { - rhl.Printf("WebUploadHandler: upload for file '%s' finished", srcfile) + rhl.Printf("WebUploadHandler: file upload for '%s/%s' finished", username, sessionid) webUploadSuccessResponse(w) return } select { case <-cancel: - rhl.Printf("WebUploadHandler: upload for file '%s' got canceld", srcfile) + rhl.Printf("WebUploadHandler: file upload for '%s/%s' got canceld", username, sessionid) webUploadErrorResponse(w, http.StatusNoContent, "canceled") return case attachmentChan <- chunk: diff --git a/web-static/socket.html b/web-static/socket.html index 9781e81..6d6236c 100644 --- a/web-static/socket.html +++ b/web-static/socket.html @@ -66,8 +66,10 @@ $('#rawmsg').text(""); this.sock = new WebSocket("ws://localhost:4080/public/socket"); this.sock_onmessage = function (event) { - $('#rawmsg').append(event.data + "\n"); msg = $.parseJSON(event.data) + if (msg.TYPE != "progress") { + $('#rawmsg').append(event.data + "
\n"); + } switch (msg.TYPE) { case "ack": $('#sessionid').val(msg.ID); diff --git a/web-static/upload.html b/web-static/upload.html index 39cfa57..21d1c48 100644 --- a/web-static/upload.html +++ b/web-static/upload.html @@ -26,21 +26,16 @@ function upload() { $('#buttonupload').attr('disabled','disabled'); - var cmdData = new FormData(); - cmdData.append("LOGIN_NAME", $('#username').val()); - cmdData.append("SESSION_ID", $('#sessionid').val()); - cmdData.append("FILENAME", $('#file').get(0).files[0]); var command = { type: "POST", - contentType: false, + contentType: "application/octet-stream", + data: $('#file').get(0).files[0], processData: false, - data: cmdData, dataType: 'json', error: function(req, status, err) { result({StatusText: status, ErrorString: err}); }, success: function(data, status, req) { result(data); } }; - $.ajax('/public/upload', command); - + $.ajax('/public/upload/' + $('#username').val() + '/' + $('#sessionid').val(), command); } function result(data) { -- cgit v0.10.2