mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
reworking errors + now using a working dir for our converter
This commit is contained in:
@@ -22,8 +22,7 @@ type App struct {
|
||||
func NewApp(version string) (*App, error) {
|
||||
c, err := config.NewAppConfig()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, &appConfigError{}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a := &App{}
|
||||
@@ -52,8 +51,8 @@ func NewApp(version string) (*App, error) {
|
||||
|
||||
func (a *App) Run() error {
|
||||
process.Load(a.config.CommandsConfig)
|
||||
logger.Infof("Gotenberg %s", a.version)
|
||||
logger.Infof("Application is starting on %s", a.Server.Addr)
|
||||
logger.Infof("Starting Gotenberg version %s", a.version)
|
||||
logger.Infof("Listening on port %s", a.config.Port)
|
||||
|
||||
return a.Server.ListenAndServe()
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@ import (
|
||||
"io/ioutil"
|
||||
"text/template"
|
||||
|
||||
"github.com/gulien/gotenberg/app/logger"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
@@ -36,23 +33,25 @@ type (
|
||||
func NewAppConfig() (*AppConfig, error) {
|
||||
fileConfig, err := loadFileConfig()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, &fileConfigError{}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &AppConfig{}
|
||||
c.Port = fileConfig.Port
|
||||
c.Logs.Level = getLoggingLevelFromFileConfig(fileConfig)
|
||||
c.Logs.Formatter = getLoggingFormatterFromFileConfig(fileConfig)
|
||||
|
||||
if c.Logs.Level == 999 {
|
||||
return nil, &wrongLoggingLevelError{}
|
||||
lvl, err := getLoggingLevelFromFileConfig(fileConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.Logs.Formatter == nil {
|
||||
return nil, &wrongLoggingFormatError{}
|
||||
formatter, err := getLoggingFormatterFromFileConfig(fileConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.Logs.Level = lvl
|
||||
c.Logs.Formatter = formatter
|
||||
|
||||
c.CommandsConfig = &CommandsConfig{}
|
||||
c.CommandsConfig.HTML = &CommandConfig{}
|
||||
c.CommandsConfig.Office = &CommandConfig{}
|
||||
@@ -61,22 +60,19 @@ func NewAppConfig() (*AppConfig, error) {
|
||||
c.CommandsConfig.Office.Timeout = fileConfig.Commands.Office.Timeout
|
||||
c.CommandsConfig.Merge.Timeout = fileConfig.Commands.Merge.Timeout
|
||||
|
||||
tmplHTML, err := getCommandTemplate(fileConfig.Commands.HTML.Template)
|
||||
tmplHTML, err := getCommandTemplate(fileConfig.Commands.HTML.Template, "HTML")
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, &wrongHTMLCommandTemplate{}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tmplOffice, err := getCommandTemplate(fileConfig.Commands.Office.Template)
|
||||
tmplOffice, err := getCommandTemplate(fileConfig.Commands.Office.Template, "Office")
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, &wrongOfficeCommandTemplate{}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tmplMerge, err := getCommandTemplate(fileConfig.Commands.Merge.Template)
|
||||
tmplMerge, err := getCommandTemplate(fileConfig.Commands.Merge.Template, "Merge")
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, &wrongMergeCommandTemplate{}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.CommandsConfig.HTML.Template = tmplHTML
|
||||
@@ -116,13 +112,11 @@ func loadFileConfig() (*fileConfig, error) {
|
||||
|
||||
data, err := ioutil.ReadFile(configurationFilePath)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, &readFileError{}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(data, &c); err != nil {
|
||||
logger.Error(err)
|
||||
return nil, &unmarshalError{}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
@@ -137,13 +131,19 @@ var levels = map[string]logrus.Level{
|
||||
"PANIC": logrus.PanicLevel,
|
||||
}
|
||||
|
||||
func getLoggingLevelFromFileConfig(c *fileConfig) logrus.Level {
|
||||
type wrongLoggingLevelError struct{}
|
||||
|
||||
func (e *wrongLoggingLevelError) Error() string {
|
||||
return "Accepted values for logging level: DEBUG, INFO, WARN, ERROR, FATAL, PANIC"
|
||||
}
|
||||
|
||||
func getLoggingLevelFromFileConfig(c *fileConfig) (logrus.Level, error) {
|
||||
l, ok := levels[c.Logs.Level]
|
||||
if !ok {
|
||||
return 999
|
||||
return 999, &wrongLoggingLevelError{}
|
||||
}
|
||||
|
||||
return l
|
||||
return l, nil
|
||||
}
|
||||
|
||||
var formatters = map[string]logrus.Formatter{
|
||||
@@ -151,17 +151,23 @@ var formatters = map[string]logrus.Formatter{
|
||||
"json": &logrus.JSONFormatter{},
|
||||
}
|
||||
|
||||
func getLoggingFormatterFromFileConfig(c *fileConfig) logrus.Formatter {
|
||||
f, ok := formatters[c.Logs.Format]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
type wrongLoggingFormatError struct{}
|
||||
|
||||
return f
|
||||
func (e *wrongLoggingFormatError) Error() string {
|
||||
return "Accepted value for logging format: text, json"
|
||||
}
|
||||
|
||||
func getCommandTemplate(command string) (*template.Template, error) {
|
||||
t, err := template.New(uuid.NewV4().String()).Parse(command)
|
||||
func getLoggingFormatterFromFileConfig(c *fileConfig) (logrus.Formatter, error) {
|
||||
f, ok := formatters[c.Logs.Format]
|
||||
if !ok {
|
||||
return nil, &wrongLoggingFormatError{}
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func getCommandTemplate(command string, commandName string) (*template.Template, error) {
|
||||
t, err := template.New(commandName).Parse(command)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package config
|
||||
|
||||
type fileConfigError struct{}
|
||||
|
||||
func (e *fileConfigError) Error() string {
|
||||
return "An error occured while trying to load the configuration file"
|
||||
}
|
||||
|
||||
type wrongLoggingLevelError struct{}
|
||||
|
||||
func (e *wrongLoggingLevelError) Error() string {
|
||||
return "Accepted values for logging level: DEBUG, INFO, WARN, ERROR, FATAL, PANIC"
|
||||
}
|
||||
|
||||
type wrongLoggingFormatError struct{}
|
||||
|
||||
func (e *wrongLoggingFormatError) Error() string {
|
||||
return "Accepted value for logging format: text, json"
|
||||
}
|
||||
|
||||
type wrongHTMLCommandTemplate struct{}
|
||||
|
||||
func (e *wrongHTMLCommandTemplate) Error() string {
|
||||
return "An error occured while trying to parse the HTML command's template"
|
||||
}
|
||||
|
||||
type wrongOfficeCommandTemplate struct{}
|
||||
|
||||
func (e *wrongOfficeCommandTemplate) Error() string {
|
||||
return "An error occured while trying to parse the Office command's template"
|
||||
}
|
||||
|
||||
type wrongMergeCommandTemplate struct{}
|
||||
|
||||
func (e *wrongMergeCommandTemplate) Error() string {
|
||||
return "An error occured while trying to parse the merge command's template"
|
||||
}
|
||||
|
||||
type readFileError struct{}
|
||||
|
||||
func (e *readFileError) Error() string {
|
||||
return "An error occured while trying to read the configuration file"
|
||||
}
|
||||
|
||||
type unmarshalError struct{}
|
||||
|
||||
func (e *unmarshalError) Error() string {
|
||||
return "An error occured while trying to decode the configuration file as YAML"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package app
|
||||
|
||||
type appConfigError struct{}
|
||||
|
||||
func (e *appConfigError) Error() string {
|
||||
return "A fatal error occured while setting up the application"
|
||||
}
|
||||
@@ -14,6 +14,7 @@ type key uint32
|
||||
const (
|
||||
contentTypeKey key = iota
|
||||
converterKey
|
||||
resultFilePathKey
|
||||
)
|
||||
|
||||
func WithContentType(r *http.Request, contentType ghttp.ContentType) *http.Request {
|
||||
@@ -24,6 +25,12 @@ func WithContentType(r *http.Request, contentType ghttp.ContentType) *http.Reque
|
||||
return r
|
||||
}
|
||||
|
||||
type contentTypeNotFoundError struct{}
|
||||
|
||||
func (e *contentTypeNotFoundError) Error() string {
|
||||
return "The 'Content-Type' was not found in request context"
|
||||
}
|
||||
|
||||
func GetContentType(r *http.Request) (ghttp.ContentType, error) {
|
||||
ct, ok := r.Context().Value(contentTypeKey).(ghttp.ContentType)
|
||||
if !ok {
|
||||
@@ -41,6 +48,12 @@ func WithConverter(r *http.Request, converter *converter.Converter) *http.Reques
|
||||
return r
|
||||
}
|
||||
|
||||
type converterNotFoundError struct{}
|
||||
|
||||
func (e *converterNotFoundError) Error() string {
|
||||
return "The converter was not found in request context"
|
||||
}
|
||||
|
||||
func GetConverter(r *http.Request) (*converter.Converter, error) {
|
||||
c, ok := r.Context().Value(converterKey).(*converter.Converter)
|
||||
if !ok {
|
||||
@@ -49,3 +62,26 @@ func GetConverter(r *http.Request) (*converter.Converter, error) {
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func WithResultFilePath(r *http.Request, resultFilePath string) *http.Request {
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, resultFilePathKey, resultFilePath)
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
type resultFilePathNotFoundError struct{}
|
||||
|
||||
func (e *resultFilePathNotFoundError) Error() string {
|
||||
return "The resultFilePath was not found in request context"
|
||||
}
|
||||
|
||||
func GetResultFilePath(r *http.Request) (string, error) {
|
||||
path, ok := r.Context().Value(resultFilePathKey).(string)
|
||||
if !ok {
|
||||
return "", &resultFilePathNotFoundError{}
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package context
|
||||
|
||||
type contentTypeNotFoundError struct{}
|
||||
|
||||
func (e *contentTypeNotFoundError) Error() string {
|
||||
return "The 'Content-Type' was not found in request context"
|
||||
}
|
||||
|
||||
type converterNotFoundError struct{}
|
||||
|
||||
func (e *converterNotFoundError) Error() string {
|
||||
return "The converter was not found in request context"
|
||||
}
|
||||
@@ -1,22 +1,36 @@
|
||||
package converter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
gfile "github.com/gulien/gotenberg/app/handlers/converter/file"
|
||||
"github.com/gulien/gotenberg/app/handlers/converter/process"
|
||||
ghttp "github.com/gulien/gotenberg/app/handlers/http"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type Converter struct {
|
||||
files []*gfile.File
|
||||
resultFilesPaths []string
|
||||
FinalFilePath string
|
||||
files []*gfile.File
|
||||
workingDir string
|
||||
}
|
||||
|
||||
type NoFileToConvertError struct{}
|
||||
|
||||
func (e *NoFileToConvertError) Error() string {
|
||||
return "There is no file to convert"
|
||||
}
|
||||
|
||||
func NewConverter(r *http.Request, contentType ghttp.ContentType) (*Converter, error) {
|
||||
c := &Converter{}
|
||||
c := &Converter{
|
||||
workingDir: fmt.Sprintf("./%s/", uuid.NewV4().String()),
|
||||
}
|
||||
|
||||
if err := os.Mkdir(c.workingDir, 0666); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch contentType {
|
||||
case ghttp.MultipartFormDataContentType:
|
||||
@@ -28,7 +42,6 @@ func NewConverter(r *http.Request, contentType ghttp.ContentType) (*Converter, e
|
||||
formData := r.MultipartForm
|
||||
files, ok := formData.File["files"]
|
||||
if !ok {
|
||||
// TODO
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -40,9 +53,8 @@ func NewConverter(r *http.Request, contentType ghttp.ContentType) (*Converter, e
|
||||
|
||||
defer file.Close()
|
||||
|
||||
f, err := gfile.NewFile(file)
|
||||
f, err := gfile.NewFile(c.workingDir, file)
|
||||
if err != nil {
|
||||
// todo err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -50,29 +62,28 @@ func NewConverter(r *http.Request, contentType ghttp.ContentType) (*Converter, e
|
||||
}
|
||||
break
|
||||
default:
|
||||
f, err := gfile.NewFile(r.Body)
|
||||
f, err := gfile.NewFile(c.workingDir, r.Body)
|
||||
if err != nil {
|
||||
// todo err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.files = append(c.files, f)
|
||||
}
|
||||
|
||||
if len(c.files) == 0 {
|
||||
return nil, &NoFileToConvertError{}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Converter) Convert() error {
|
||||
if len(c.files) == 0 {
|
||||
return &noFileToConvertError{}
|
||||
}
|
||||
|
||||
func (c *Converter) Convert() (string, error) {
|
||||
var filesPaths []string
|
||||
for _, f := range c.files {
|
||||
if f.Type != gfile.PDFType {
|
||||
path, err := process.ExecConversion(f)
|
||||
path, err := process.ExecConversion(c.workingDir, f)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
filesPaths = append(filesPaths, path)
|
||||
@@ -81,37 +92,21 @@ func (c *Converter) Convert() error {
|
||||
}
|
||||
}
|
||||
|
||||
path, err := process.ExecMerge(filesPaths)
|
||||
if err != nil {
|
||||
return err
|
||||
if len(filesPaths) == 0 {
|
||||
return filesPaths[0], nil
|
||||
}
|
||||
|
||||
c.resultFilesPaths = filesPaths
|
||||
c.FinalFilePath = path
|
||||
path, err := process.ExecMerge(c.workingDir, filesPaths)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return nil
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (c *Converter) Clear() error {
|
||||
for _, f := range c.files {
|
||||
err := os.Remove(f.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, path := range c.resultFilesPaths {
|
||||
err := os.Remove(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.FinalFilePath != "" {
|
||||
err := os.Remove(c.FinalFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(c.workingDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package converter
|
||||
|
||||
type noFileToConvertError struct{}
|
||||
|
||||
func (e *noFileToConvertError) Error() string {
|
||||
return "There is no file to convert"
|
||||
}
|
||||
@@ -10,30 +10,22 @@ import (
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type (
|
||||
File struct {
|
||||
Type FileType
|
||||
Path string
|
||||
}
|
||||
type File struct {
|
||||
Type FileType
|
||||
Path string
|
||||
}
|
||||
|
||||
FileType string
|
||||
|
||||
FileExt string
|
||||
)
|
||||
type FileType uint32
|
||||
|
||||
const (
|
||||
PDFType FileType = "PDF"
|
||||
HTMLType FileType = "HTML"
|
||||
OfficeType FileType = "Office"
|
||||
|
||||
PDFExt FileExt = ".pdf"
|
||||
HTMLExt FileExt = ".html"
|
||||
OfficeExt FileExt = ""
|
||||
PDFType FileType = iota
|
||||
HTMLType
|
||||
OfficeType
|
||||
)
|
||||
|
||||
func NewFile(r io.Reader) (*File, error) {
|
||||
func NewFile(workingDir string, r io.Reader) (*File, error) {
|
||||
f := &File{
|
||||
Path: MakeFilePath(),
|
||||
Path: MakeFilePath(workingDir),
|
||||
}
|
||||
|
||||
file, err := os.Create(f.Path)
|
||||
@@ -58,7 +50,7 @@ func NewFile(r io.Reader) (*File, error) {
|
||||
|
||||
f.Type = t
|
||||
|
||||
f, err = reworkFilePath(f)
|
||||
f, err = reworkFilePath(workingDir, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -66,8 +58,8 @@ func NewFile(r io.Reader) (*File, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func MakeFilePath() string {
|
||||
return fmt.Sprintf("./%s", uuid.NewV4().String())
|
||||
func MakeFilePath(workingDir string) string {
|
||||
return fmt.Sprintf("%s%s", workingDir, uuid.NewV4().String())
|
||||
}
|
||||
|
||||
var filesTypes = map[ghttp.ContentType]FileType{
|
||||
@@ -77,36 +69,54 @@ var filesTypes = map[ghttp.ContentType]FileType{
|
||||
ghttp.ZipContentType: OfficeType,
|
||||
}
|
||||
|
||||
type fileTypeNotFound struct{}
|
||||
|
||||
func (e *fileTypeNotFound) Error() string {
|
||||
return "The file type was not found for the given 'Content-Type'"
|
||||
}
|
||||
|
||||
func findFileType(f *os.File) (FileType, error) {
|
||||
ct, err := ghttp.SniffContentType(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return 999, err
|
||||
}
|
||||
|
||||
t, ok := filesTypes[ct]
|
||||
if !ok {
|
||||
// TODO error
|
||||
return "", nil
|
||||
return 999, &fileTypeNotFound{}
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
type FileExt string
|
||||
|
||||
const (
|
||||
PDFExt FileExt = ".pdf"
|
||||
HTMLExt FileExt = ".html"
|
||||
OfficeExt FileExt = ""
|
||||
)
|
||||
|
||||
var filesExtensions = map[FileType]FileExt{
|
||||
PDFType: PDFExt,
|
||||
HTMLType: HTMLExt,
|
||||
OfficeType: OfficeExt,
|
||||
}
|
||||
|
||||
func reworkFilePath(f *File) (*File, error) {
|
||||
type fileExtNotFound struct{}
|
||||
|
||||
func (e *fileExtNotFound) Error() string {
|
||||
return "The file extension was not found for the given file type"
|
||||
}
|
||||
|
||||
func reworkFilePath(workingDir string, f *File) (*File, error) {
|
||||
ext, ok := filesExtensions[f.Type]
|
||||
if !ok {
|
||||
// TODO error
|
||||
return nil, nil
|
||||
return nil, &fileExtNotFound{}
|
||||
}
|
||||
|
||||
if ext != OfficeExt {
|
||||
newPath := fmt.Sprintf("./%s%s", MakeFilePath(), ext)
|
||||
newPath := fmt.Sprintf("%s%s", MakeFilePath(workingDir), ext)
|
||||
|
||||
err := os.Rename(f.Path, newPath)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package process
|
||||
|
||||
type impossibleConversionError struct{}
|
||||
|
||||
func (e *impossibleConversionError) Error() string {
|
||||
return "Impossible conversion"
|
||||
}
|
||||
|
||||
type commandTimeoutError struct{}
|
||||
|
||||
func (e *commandTimeoutError) Error() string {
|
||||
return "The command has reached timeout"
|
||||
}
|
||||
@@ -17,19 +17,21 @@ func Load(config *config.CommandsConfig) {
|
||||
commandsConfig = config
|
||||
}
|
||||
|
||||
func Reset() {
|
||||
commandsConfig = nil
|
||||
}
|
||||
|
||||
type conversionData struct {
|
||||
FilePath string
|
||||
ResultFilePath string
|
||||
}
|
||||
|
||||
func ExecConversion(file *gfile.File) (string, error) {
|
||||
type impossibleConversionError struct{}
|
||||
|
||||
func (e *impossibleConversionError) Error() string {
|
||||
return "Impossible conversion"
|
||||
}
|
||||
|
||||
func ExecConversion(workingDir string, file *gfile.File) (string, error) {
|
||||
cmdData := &conversionData{
|
||||
FilePath: file.Path,
|
||||
ResultFilePath: fmt.Sprintf("%s%s", gfile.MakeFilePath(), gfile.PDFExt),
|
||||
ResultFilePath: fmt.Sprintf("%s%s", gfile.MakeFilePath(workingDir), gfile.PDFExt),
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -68,10 +70,10 @@ type mergeData struct {
|
||||
ResultFilePath string
|
||||
}
|
||||
|
||||
func ExecMerge(filesPaths []string) (string, error) {
|
||||
func ExecMerge(workingDir string, filesPaths []string) (string, error) {
|
||||
cmdData := &mergeData{
|
||||
FilesPaths: filesPaths,
|
||||
ResultFilePath: fmt.Sprintf("%s%s", gfile.MakeFilePath(), gfile.PDFExt),
|
||||
ResultFilePath: fmt.Sprintf("%s%s", gfile.MakeFilePath(workingDir), gfile.PDFExt),
|
||||
}
|
||||
|
||||
cmdTemplate := commandsConfig.Merge.Template
|
||||
@@ -90,8 +92,13 @@ func ExecMerge(filesPaths []string) (string, error) {
|
||||
return cmdData.ResultFilePath, nil
|
||||
}
|
||||
|
||||
type commandTimeoutError struct{}
|
||||
|
||||
func (e *commandTimeoutError) Error() string {
|
||||
return "The command has reached timeout"
|
||||
}
|
||||
|
||||
func execCommand(command string, timeout int) error {
|
||||
// Wait for the process to finish or kill it after a timeout.
|
||||
cmd := exec.Command("/bin/sh", "-c", command)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
@@ -102,6 +109,7 @@ func execCommand(command string, timeout int) error {
|
||||
done <- cmd.Wait()
|
||||
}()
|
||||
|
||||
// wait for the process to finish or kill it after a timeout.
|
||||
select {
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
if err := cmd.Process.Kill(); err != nil {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package handlers
|
||||
|
||||
type requestHasNoContentError struct{}
|
||||
|
||||
func (e *requestHasNoContentError) Error() string {
|
||||
return "Request has not content"
|
||||
}
|
||||
@@ -18,6 +18,12 @@ func GetHandlersChain() http.Handler {
|
||||
return alice.New(enforceContentLengthHandler, enforceContentTypeHandler, convertHandler, serveHandler).ThenFunc(clearHandler)
|
||||
}
|
||||
|
||||
type requestHasNoContentError struct{}
|
||||
|
||||
func (e *requestHasNoContentError) Error() string {
|
||||
return "Request has not content"
|
||||
}
|
||||
|
||||
// enforeContentLengthHandler checks if the request has content.
|
||||
func enforceContentLengthHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -61,12 +67,17 @@ func convertHandler(next http.Handler) http.Handler {
|
||||
|
||||
c, err := converter.NewConverter(r, ct)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
if noFileToConvertError, ok := err.(*converter.NoFileToConvertError); ok {
|
||||
http.Error(w, noFileToConvertError.Error(), http.StatusBadRequest)
|
||||
} else {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = c.Convert()
|
||||
path, err := c.Convert()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
logger.Error(err)
|
||||
@@ -74,6 +85,7 @@ func convertHandler(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
r = context.WithConverter(r, c)
|
||||
r = context.WithResultFilePath(r, path)
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
@@ -82,13 +94,13 @@ func convertHandler(next http.Handler) http.Handler {
|
||||
// serveHandler simply serves the created PDF.
|
||||
func serveHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := context.GetConverter(r)
|
||||
path, err := context.GetResultFilePath(r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
logger.Error(err)
|
||||
}
|
||||
|
||||
reader, err := os.Open(c.FinalFilePath)
|
||||
reader, err := os.Open(path)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
logger.Error(err)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package http
|
||||
|
||||
import "fmt"
|
||||
|
||||
type notAuthorizedContentTypeError struct{}
|
||||
|
||||
func (e *notAuthorizedContentTypeError) Error() string {
|
||||
return fmt.Sprintf("Accepted values for 'Content-Type': %s, %s, %s, %s", PDFContentType, HTMLContentType, OctetStreamContentType, MultipartFormDataContentType)
|
||||
}
|
||||
|
||||
type notAuthorizedFileContentTypeError struct{}
|
||||
|
||||
func (e *notAuthorizedFileContentTypeError) Error() string {
|
||||
return fmt.Sprintf("Unable to detect a file 'Content-Type'")
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -16,6 +17,12 @@ const (
|
||||
MultipartFormDataContentType ContentType = "multipart/form-data"
|
||||
)
|
||||
|
||||
type notAuthorizedContentTypeError struct{}
|
||||
|
||||
func (e *notAuthorizedContentTypeError) Error() string {
|
||||
return fmt.Sprintf("Accepted values for 'Content-Type': %s, %s, %s, %s", PDFContentType, HTMLContentType, OctetStreamContentType, MultipartFormDataContentType)
|
||||
}
|
||||
|
||||
func FindAuthorizedContentType(h http.Header) (ContentType, error) {
|
||||
ct := findContentType(h.Get("Content-Type"), HTMLContentType, OctetStreamContentType, MultipartFormDataContentType)
|
||||
if ct == "" {
|
||||
@@ -25,6 +32,12 @@ func FindAuthorizedContentType(h http.Header) (ContentType, error) {
|
||||
return ct, nil
|
||||
}
|
||||
|
||||
type notAuthorizedFileContentTypeError struct{}
|
||||
|
||||
func (e *notAuthorizedFileContentTypeError) Error() string {
|
||||
return fmt.Sprintf("Unable to detect a file 'Content-Type'")
|
||||
}
|
||||
|
||||
func SniffContentType(f *os.File) (ContentType, error) {
|
||||
// only the first 512 bytes are used to sniff the content type.
|
||||
buffer := make([]byte, 512)
|
||||
|
||||
12
main.go
12
main.go
@@ -15,7 +15,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gulien/gotenberg/app"
|
||||
"github.com/gulien/gotenberg/app/handlers/converter/process"
|
||||
"github.com/gulien/gotenberg/app/logger"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -29,7 +28,7 @@ var version = "master"
|
||||
func main() {
|
||||
a, err := app.NewApp(version)
|
||||
if err != nil {
|
||||
resetState()
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
logger.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -37,7 +36,7 @@ func main() {
|
||||
// runs our server in a goroutine so that it doesn't block.
|
||||
go func() {
|
||||
if err = a.Run(); err != nil {
|
||||
resetState()
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
logger.Panic(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -59,13 +58,8 @@ func main() {
|
||||
// doesn't block if no connections, but will otherwise wait
|
||||
// until the timeout deadline.
|
||||
a.Server.Shutdown(ctx)
|
||||
resetState()
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
|
||||
logger.Info("Bye!")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func resetState() {
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
process.Reset()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user