<commit_before>package server

import (
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	"image"
	"io"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"net/http"
	"time"
)

const JpegMaximumQuality = 100
const ImageCacheDuration = 315360000

var Connection *mgo.Session
var Configuration *Config

// VarsHandler is a simple wrapper so the request params can be injected into the main handler
type VarsHandler func(http.ResponseWriter, *http.Request, *ServerConfiguration)

// ServeHTTP wraps the imageHandler function and validates request parameters
// in order to create a ServerConfiguration object
func (h VarsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	requestConfig, validateError := CreateConfigurationFromVars(r, vars)

	if validateError != nil {
		log.Printf("%d invalid request parameters given.\n", http.StatusNotFound)
		w.WriteHeader(http.StatusNotFound)
		return
	}

	h(w, r, requestConfig)
}

// addImageMetaData adds data to the image the image
func addImageMetaData(targetImage *mgo.GridFile, imageData image.Image, imageFormat string, originalImage *mgo.GridFile, entry *Entry) {
	width := imageData.Bounds().Dx()
	height := imageData.Bounds().Dy()
	originalRef := mgo.DBRef{"fs.files", originalImage.Id(), ""}

	metadata := bson.M{
		"width":            width,
		"height":           height,
		"original":         originalRef,
		"originalFilename": originalImage.Name(),
		"resizeType":       entry.Type,
		"size":             fmt.Sprintf("%dx%d", width, height)}

	targetImage.SetContentType(fmt.Sprintf("image/%s", imageFormat))
	targetImage.SetMeta(metadata)
}

// isModified returns true if the file must be delivered, false otherwise.
func isModified(file *mgo.GridFile, header *http.Header) bool {
	md5 := file.MD5()
	modifiedHeader := header.Get("If-Modified-Since")
	modifiedTime := time.Now()

	if modifiedHeader != "" {
		modifiedTime, _ = time.Parse(time.RFC1123, modifiedHeader)
	}

	// normalize upload date to use the same format as the browser
	uploadDate, _ := time.Parse(time.RFC1123, file.UploadDate().Format(time.RFC1123))

	if header.Get("Cache-Control") == "no-cache" {
		log.Printf("Is modified, because caching not enabled.")
		return true
	}

	if uploadDate.After(modifiedTime) {
		log.Printf("Is modified, because upload date after modified date.\n")
		return true
	}

	if md5 != header.Get("If-None-Match") {
		log.Printf("Is modified, because md5 mismatch. %s != %s", md5, header.Get("If-None-Match"))
		return true
	}

	return false
}

// setCacheHeaders sets the cache headers into the http.ResponseWriter
func setCacheHeaders(file *mgo.GridFile, w http.ResponseWriter) {
	w.Header().Set("Etag", file.MD5())
	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", ImageCacheDuration))
	d, _ := time.ParseDuration(fmt.Sprintf("%ds", ImageCacheDuration))

	expires := file.UploadDate().Add(d)

	w.Header().Set("Last-Modified", file.UploadDate().Format(time.RFC1123))
	w.Header().Set("Expires", expires.Format(time.RFC1123))
	w.Header().Set("Date", file.UploadDate().Format(time.RFC1123))
}

// imageHandler the main handler
func imageHandler(w http.ResponseWriter, r *http.Request, requestConfig *ServerConfiguration) {
	log.Printf("Request on %s", r.URL)

	if Connection == nil {
		log.Printf("Connection is not set.")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if Configuration == nil {
		log.Printf("Configuration object is not set.")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	gridfs := Connection.DB(requestConfig.Database).GridFS("fs")

	resizeEntry, _ := Configuration.GetEntryByName(requestConfig.FormatName)
	foundImage, _ := FindImageByParentFilename(requestConfig.Filename, resizeEntry, gridfs)

	// case that we do not want resizing and did not find any image
	if foundImage == nil && resizeEntry == nil {
		log.Printf("%d invalid request parameters given.\n", http.StatusNotFound)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// we found a image but did not want resizing
	if foundImage != nil {
		if !isModified(foundImage, &r.Header) {
			w.WriteHeader(http.StatusNotModified)
			log.Printf("%d Returning cached image.\n", http.StatusNotModified)
			return
		}

		setCacheHeaders(foundImage, w)

		io.Copy(w, foundImage)
		foundImage.Close()
		log.Printf("%d Image found, no resizing.\n", http.StatusOK)
		return
	}

	if foundImage == nil && resizeEntry != nil {
		// generate new image
		foundImage, _ = FindImageByParentFilename(requestConfig.Filename, nil, gridfs)

		if foundImage == nil {
			log.Printf("%d Could not find original image.\n", http.StatusNotFound)
			w.WriteHeader(http.StatusNotFound)
			return
		}

		resizedImage, imageFormat, imageErr := ResizeImageFromGridfs(foundImage, resizeEntry)

		// in this case, resizing for this image does not work, therefore, we at least return the original image
		if imageErr != nil {

			// this might be a problem at the moment, go does not support interlaced pngs
			// http://code.google.com/p/go/issues/detail?id=6293
			// at the moment, we return a not found...
			log.Printf("%d image could not be decoded.\n", http.StatusNotFound)
			w.WriteHeader(http.StatusNotFound)
			return
		}

		// return the image to the client if all cache headers could be set
		targetfile, _ := gridfs.Create(GetRandomFilename(imageFormat))
		encodeErr := EncodeImage(targetfile, *resizedImage, imageFormat)<commit_msg>Explicit garbage collection call to fix memory leak<commit_after>package server

import (
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	"image"
	"io"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"net/http"
	"runtime"
	"time"
)

const JpegMaximumQuality = 100
const ImageCacheDuration = 315360000

var Connection *mgo.Session
var Configuration *Config

// VarsHandler is a simple wrapper so the request params can be injected into the main handler
type VarsHandler func(http.ResponseWriter, *http.Request, *ServerConfiguration)

// ServeHTTP wraps the imageHandler function and validates request parameters
// in order to create a ServerConfiguration object
func (h VarsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	requestConfig, validateError := CreateConfigurationFromVars(r, vars)

	if validateError != nil {
		log.Printf("%d invalid request parameters given.\n", http.StatusNotFound)
		w.WriteHeader(http.StatusNotFound)
		return
	}

	h(w, r, requestConfig)
}

// addImageMetaData adds data to the image the image
func addImageMetaData(targetImage *mgo.GridFile, imageData image.Image, imageFormat string, originalImage *mgo.GridFile, entry *Entry) {
	width := imageData.Bounds().Dx()
	height := imageData.Bounds().Dy()
	originalRef := mgo.DBRef{"fs.files", originalImage.Id(), ""}

	metadata := bson.M{
		"width":            width,
		"height":           height,
		"original":         originalRef,
		"originalFilename": originalImage.Name(),
		"resizeType":       entry.Type,
		"size":             fmt.Sprintf("%dx%d", width, height)}

	targetImage.SetContentType(fmt.Sprintf("image/%s", imageFormat))
	targetImage.SetMeta(metadata)
}

// isModified returns true if the file must be delivered, false otherwise.
func isModified(file *mgo.GridFile, header *http.Header) bool {
	md5 := file.MD5()
	modifiedHeader := header.Get("If-Modified-Since")
	modifiedTime := time.Now()

	if modifiedHeader != "" {
		modifiedTime, _ = time.Parse(time.RFC1123, modifiedHeader)
	}

	// normalize upload date to use the same format as the browser
	uploadDate, _ := time.Parse(time.RFC1123, file.UploadDate().Format(time.RFC1123))

	if header.Get("Cache-Control") == "no-cache" {
		log.Printf("Is modified, because caching not enabled.")
		return true
	}

	if uploadDate.After(modifiedTime) {
		log.Printf("Is modified, because upload date after modified date.\n")
		return true
	}

	if md5 != header.Get("If-None-Match") {
		log.Printf("Is modified, because md5 mismatch. %s != %s", md5, header.Get("If-None-Match"))
		return true
	}

	return false
}

// setCacheHeaders sets the cache headers into the http.ResponseWriter
func setCacheHeaders(file *mgo.GridFile, w http.ResponseWriter) {
	w.Header().Set("Etag", file.MD5())
	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", ImageCacheDuration))
	d, _ := time.ParseDuration(fmt.Sprintf("%ds", ImageCacheDuration))

	expires := file.UploadDate().Add(d)

	w.Header().Set("Last-Modified", file.UploadDate().Format(time.RFC1123))
	w.Header().Set("Expires", expires.Format(time.RFC1123))
	w.Header().Set("Date", file.UploadDate().Format(time.RFC1123))
}

// imageHandler the main handler
func imageHandler(w http.ResponseWriter, r *http.Request, requestConfig *ServerConfiguration) {
	log.Printf("Request on %s", r.URL)

	defer func() {
		runtime.GC()
	}()

	if Connection == nil {
		log.Printf("Connection is not set.")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if Configuration == nil {
		log.Printf("Configuration object is not set.")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	gridfs := Connection.DB(requestConfig.Database).GridFS("fs")

	resizeEntry, _ := Configuration.GetEntryByName(requestConfig.FormatName)
	foundImage, _ := FindImageByParentFilename(requestConfig.Filename, resizeEntry, gridfs)

	// case that we do not want resizing and did not find any image
	if foundImage == nil && resizeEntry == nil {
		log.Printf("%d invalid request parameters given.\n", http.StatusNotFound)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// we found a image but did not want resizing
	if foundImage != nil {
		if !isModified(foundImage, &r.Header) {
			w.WriteHeader(http.StatusNotModified)
			log.Printf("%d Returning cached image.\n", http.StatusNotModified)
			return
		}

		setCacheHeaders(foundImage, w)

		io.Copy(w, foundImage)
		foundImage.Close()
		log.Printf("%d Image found, no resizing.\n", http.StatusOK)
		return
	}

	if foundImage == nil && resizeEntry != nil {
		// generate new image
		foundImage, _ = FindImageByParentFilename(requestConfig.Filename, nil, gridfs)

		if foundImage == nil {
			log.Printf("%d Could not find original image.\n", http.StatusNotFound)
			w.WriteHeader(http.StatusNotFound)
			return
		}

		resizedImage, imageFormat, imageErr := ResizeImageFromGridfs(foundImage, resizeEntry)

		// in this case, resizing for this image does not work, therefore, we at least return the original image
		if imageErr != nil {

			// this might be a problem at the moment, go does not support interlaced pngs
			// http://code.google.com/p/go/issues/detail?id=6293
			// at the moment, we return a not found...
			log.Printf("%d image could not be decoded.\n", http.StatusNotFound)
			w.WriteHeader(http.StatusNotFound)
			return
		}

		// return the image to the client if all cache headers could be set
		targetfile, _ := gridfs.Create(GetRandomFilename(imageFormat))

		encodeErr := EncodeImage(targetfile, *resizedImage, imageFormat)
{'fraction_non_alphanumeric': 0.12647895074750068, 'fraction_numerical': 0.00880491607814363, 'mean_word_length': 4.371428571428571, 'pattern_counts': {'":': 12, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '119052', 'n_tokens_mistral': 3637, 'n_tokens_neox': 3398, 'n_words': 904}