// ServeHTTP はリクエストに応じて処理を行いレスポンスする。 func (h *Handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { t := log.Start() defer log.End(t) if req.URL.Path != "/" { resp.WriteHeader(http.StatusNotFound) fmt.Fprint(resp, "Not Found") log.Infof("not found") return } if err := h.operate(resp, req); err != nil { log.Infof("fail to operate: error=%v", err) resp.WriteHeader(http.StatusBadRequest) enc := json.NewEncoder(resp) obj := map[string]string{ "message": err.Error(), } if err := enc.Encode(obj); err != nil { log.Errorf("fail to encode error message to JSON: error=%v", err) } return } log.Infof("ok") }
func main() { // defer func() { // if r := recover(); r != nil { // log.Println("RECOVER!!!!!") // w, err := os.Create("panic.log") // if err != nil { // os.Exit(1) // } // os.Stderr = w // debug.PrintStack() // } // runtime.SetBlockProfileRate(1) // go func() { // log.Println(http.ListenAndServe("0.0.0.0:6060", nil)) // }() runtime.GOMAXPROCS(runtime.NumCPU()) filename, err := log.Init() if err != nil { panic(err) } if filename != "" { log.Infof("%s is created", filename) } if err := fetcher.Init(); err != nil { panic(err) } if err := server.Start(); err != nil { panic(err) } }
func TestStartAndEnd(t *testing.T) { os.Setenv(log.EnvLogFilename, file) filename, err := log.Init() if err != nil { t.Fatalf("fail to init: error=%v", err) } if err := os.Unsetenv(log.EnvLogFilename); err != nil { t.Fatalf("fail to unset env: error=%v", err) } defer func() { if err := recover(); err != nil { fmt.Println(err) } }() timer := log.Start() log.Debug("debug") log.Print("print") log.Info("info") log.Warn("warn") log.Warning("warning") log.Error("error") log.Debugf("debug%s", "f") log.Printf("print%s", "f") log.Infof("info%s", "f") log.Warnf("warn%s", "f") log.Warningf("warning%s", "f") log.Errorf("error%s", "f") log.Debugln("debugln") log.Println("println") log.Infoln("infoln") log.Warnln("warnln") log.Warningln("warningln") log.Errorln("errorln") log.End(timer) expected := []map[string]string{ map[string]string{"level": "info", "msg": "start"}, map[string]string{"level": "info", "msg": "print"}, map[string]string{"level": "info", "msg": "info"}, map[string]string{"level": "warning", "msg": "warn"}, map[string]string{"level": "warning", "msg": "warning"}, map[string]string{"level": "error", "msg": "error"}, map[string]string{"level": "info", "msg": "printf"}, map[string]string{"level": "info", "msg": "infof"}, map[string]string{"level": "warning", "msg": "warnf"}, map[string]string{"level": "warning", "msg": "warningf"}, map[string]string{"level": "error", "msg": "errorf"}, map[string]string{"level": "info", "msg": "println"}, map[string]string{"level": "info", "msg": "infoln"}, map[string]string{"level": "warning", "msg": "warnln"}, map[string]string{"level": "warning", "msg": "warningln"}, map[string]string{"level": "error", "msg": "errorln"}, map[string]string{"level": "info", "msg": "end"}, } bufs, err := ioutil.ReadFile(filename) if err != nil { t.Fatalf("fail to read file: error=%v", err) } lines := strings.Split(string(bufs), "\n") l := len(lines) for i, line := range lines { // fmt.Println(i, line) if i == l-1 { break } var obj map[string]interface{} if err := json.Unmarshal([]byte(line), &obj); err != nil { t.Fatalf("fail to decode json") } e := expected[i] // fmt.Println(i, e) if obj["level"] != e["level"] { t.Fatalf("wrong level: expected %s, but actual %s", e["level"], obj["level"]) } if obj["msg"] != e["msg"] { t.Fatalf("wrong msg: expected %s, but actual %s", e["msg"], obj["msg"]) } } if err := log.Close(); err != nil { t.Fatalf("fail to close log: error=%v", err) } }
// operate は手続き的に一連のリサイズ処理を行う。 // エラーを画一的に扱うためにメソッドとして切り分けを行っている func (h *Handler) operate(resp http.ResponseWriter, req *http.Request) error { t := log.Start() defer log.End(t) // 1. URLクエリからリクエストされているオプションを抽出する i, err := storage.NewImage(req.URL.Query(), h.Hosts) if err != nil { return err } // 3. バリデート済みオプションでリサイズをしたキャッシュがあるか調べる // 4. キャッシュがあればリサイズ画像のURLにリダイレクトする cache := storage.Image{} h.Storage.Where(&storage.Image{ ValidatedHash: i.ValidatedHash, ValidatedWidth: i.ValidatedWidth, ValidatedHeight: i.ValidatedHeight, ValidatedMethod: i.ValidatedMethod, ValidatedFormat: i.ValidatedFormat, ValidatedQuality: i.ValidatedQuality, }).First(&cache) log.Debugln(cache.ID) if cache.ID != 0 { log.Infof("validated cache %+v exists, requested with %+v", cache, i) url := h.Uploader.CreateURL(cache.Filename) http.Redirect(resp, req, url, http.StatusSeeOther) return nil } log.Infof("validated cache doesn't exist, requested with %+v", i) // 5. 元画像を取得する // 6. リサイズの前処理をする filename, err := fetcher.Fetch(i.ValidatedURL) defer func() { if err := fetcher.Clean(filename); err != nil { log.Errorf("fail to clean fetched file: %s", filename) } }() if err != nil { return err } var b []byte buf := bytes.NewBuffer(b) p := processor.New() pixels, err := p.Preprocess(filename) if err != nil { return err } // 7. 正規化する // 8. 正規化済みのオプションでリサイズをしたことがあるか調べる // 9. あればリサイズ画像のURLにリダイレクトする i, err = i.Normalize(pixels.Bounds().Size()) if err != nil { return err } cache = storage.Image{} h.Storage.Where(&storage.Image{ NormalizedHash: i.NormalizedHash, DestWidth: i.DestWidth, DestHeight: i.DestHeight, ValidatedMethod: i.ValidatedMethod, ValidatedFormat: i.ValidatedFormat, ValidatedQuality: i.ValidatedQuality, }).First(&cache) if cache.ID != 0 { log.Infof("normalized cache %+v exists, requested with %+v", cache, i) url := h.Uploader.CreateURL(cache.Filename) http.Redirect(resp, req, url, http.StatusSeeOther) return nil } log.Infof("normalized cache doesn't exist, requested with %+v", i) // 10. リサイズする // 11. ファイルオブジェクトの処理結果フィールドを埋める // 12. レスポンスする size, err := p.Process(pixels, buf, i) if err != nil { return err } b = buf.Bytes() i.ETag = fmt.Sprintf("%x", md5.Sum(b)) i.Filename = i.CreateFilename() i.ContentType = contentTypes[i.ValidatedFormat] i.CanvasWidth = size.X i.CanvasHeight = size.Y resp.Header().Add("Content-Type", i.ContentType) io.Copy(resp, bufio.NewReader(buf)) // レスポンスを完了させるために非同期に処理する go h.save(b, i) return nil }