Skip to content

Commit

Permalink
Merge pull request #37 from klippa-app/feature/improve-multi-thread-p…
Browse files Browse the repository at this point in the history
…rotection

Improve multi thread protection
  • Loading branch information
jerbob92 authored Feb 6, 2022
2 parents f5e98d8 + 920006a commit 7e1023a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 52 deletions.
7 changes: 7 additions & 0 deletions code_generation/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ type GenerateDataMethod struct {
Output string
}

func (m *GenerateDataMethod) IsMultiThreaded() bool {
if m.Name == "FPDFBitmap_CreateEx" || m.Name == "FSDK_SetUnSpObjProcessHandler" || m.Name == "FSDK_SetTimeFunction" || m.Name == "FSDK_SetLocaltimeFunction" {
return true
}
return false
}

type GenerateData struct {
Methods []GenerateDataMethod
}
Expand Down
41 changes: 32 additions & 9 deletions code_generation/templates/multi_threaded.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,52 @@ import (
)
{{ range $method := .Methods }}
func (i *pdfiumInstance) {{ $method.Name }}(request *requests.{{ $method.Input }}) (*responses.{{ $method.Output }}, error) {
{{ if eq $method.IsMultiThreaded true -}}
return nil, errors.New("unsupported method on multi-threaded usage")
{{- else -}}
if i.closed {
return nil, errors.New("instance is closed")
}

{{ if eq $method.Name "FPDF_LoadCustomDocument" -}}
// Since multi-threaded usage implements gRPC, it can't serialize the reader onto that.
// To make it support the full interface, we just completely read the file into memory.
fileData, err := ioutil.ReadAll(request.Reader)
if err != nil {
return nil, err
}

// To make it support the full interface, we just rewrite it to OpenDocument,
// and OpenDocument just fully reads the io.ReadSeeker into an byte array.
doc, err := i.OpenDocument(&requests.OpenDocument{
File: &fileData,
Password: request.Password,
FileReader: request.Reader,
FileReaderSize: request.Size,
Password: request.Password,
})
if err != nil {
return nil, err
}

return &responses.FPDF_LoadCustomDocument{Document: doc.Document}, nil
{{- else -}}
{{- else if eq $method.Name "OpenDocument" -}}
// Since multi-threaded usage implements gRPC, it can't serialize the reader onto that.
// To make it support the full interface, we just fully reads the io.ReadSeeker into
// an byte array.
if request.FileReader != nil {
fileData, err := ioutil.ReadAll(request.FileReader)
if err != nil {
return nil, err
}
request.FileReader = nil
request.FileReaderSize = 0
request.File = &fileData
}
return i.worker.plugin.{{ $method.Name }}(request)
{{- else if eq $method.Name "FPDF_SaveWithVersion" -}}
return i.worker.plugin.{{ $method.Name }}(request)
{{- else if eq $method.Name "FPDF_SaveAsCopy" -}}
if request.FileWriter != nil {
return nil, errors.New("using a file-writer is not supported on multi-threaded usage")
}

return i.worker.plugin.{{ $method.Name }}(request)
{{- else -}}
return i.worker.plugin.{{ $method.Name }}(request)
{{- end }}
{{- end }}
}
{{end}}
53 changes: 25 additions & 28 deletions multi_threaded/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 1 addition & 6 deletions multi_threaded/multi_threaded_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package multi_threaded_test

import (
"github.com/klippa-app/go-pdfium/multi_threaded"
"log"
"os"
"time"

"github.com/klippa-app/go-pdfium/multi_threaded"
"github.com/klippa-app/go-pdfium/shared_tests"

. "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -41,12 +40,8 @@ var _ = AfterSuite(func() {
err := shared_tests.PdfiumInstance.Close()
Expect(err).To(BeNil())

log.Println("Closed instance")

err = shared_tests.PdfiumPool.Close()
Expect(err).To(BeNil())

log.Println("Closed pool")
})

var _ = Describe("Multi Threaded", func() {
Expand Down
22 changes: 13 additions & 9 deletions pdfium.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ type Pdfium interface {
// Start instance functions.

// OpenDocument returns a PDFium references for the given file data.
// This is a gateway to FPDF_LoadMemDocument, FPDF_LoadMemDocument64, FPDF_LoadDocument and FPDF_LoadCustomDocument.
// This is a gateway to FPDF_LoadMemDocument, FPDF_LoadMemDocument64,
// FPDF_LoadDocument and FPDF_LoadCustomDocument. Please note that
// FPDF_LoadCustomDocument will only work efficiently on single-threaded
// usage, on multi-threaded this will just fully read from the reader
// into a byte array before it's being sent over to PDFium.
// This method already checks FPDF_GetLastError internally for the result.
OpenDocument(request *requests.OpenDocument) (*responses.OpenDocument, error)

// Close closes the instance.
Expand Down Expand Up @@ -127,20 +132,17 @@ type Pdfium interface {

// FPDF_LoadDocument opens and load a PDF document from a file path.
// Loaded document can be closed by FPDF_CloseDocument().
// If this function fails, you can use FPDF_GetLastError() to retrieve
// the reason why it failed.
// This method already checks FPDF_GetLastError internally for the result.
FPDF_LoadDocument(request *requests.FPDF_LoadDocument) (*responses.FPDF_LoadDocument, error)

// FPDF_LoadMemDocument opens and load a PDF document from memory.
// Loaded document can be closed by FPDF_CloseDocument().
// If this function fails, you can use FPDF_GetLastError() to retrieve
// the reason why it failed.
// This method already checks FPDF_GetLastError internally for the result.
FPDF_LoadMemDocument(request *requests.FPDF_LoadMemDocument) (*responses.FPDF_LoadMemDocument, error)

// FPDF_LoadMemDocument64 opens and load a PDF document from memory.
// Loaded document can be closed by FPDF_CloseDocument().
// If this function fails, you can use FPDF_GetLastError() to retrieve
// the reason why it failed.
// This method already checks FPDF_GetLastError internally for the result.
FPDF_LoadMemDocument64(request *requests.FPDF_LoadMemDocument64) (*responses.FPDF_LoadMemDocument64, error)

// FPDF_LoadCustomDocument loads a PDF document from a custom access descriptor.
Expand All @@ -150,15 +152,17 @@ type Pdfium interface {
// over gRPC. The single-threaded usage will actually efficiently walk over
// the PDF as it's being used by PDFium.
// Loaded document can be closed by FPDF_CloseDocument().
// If this function fails, you can use FPDF_GetLastError() to retrieve
// the reason why it failed.
// This method already checks FPDF_GetLastError internally for the result.
FPDF_LoadCustomDocument(request *requests.FPDF_LoadCustomDocument) (*responses.FPDF_LoadCustomDocument, error)

// FPDF_CloseDocument closes the references, releases the resources.
FPDF_CloseDocument(request *requests.FPDF_CloseDocument) (*responses.FPDF_CloseDocument, error)

// FPDF_GetLastError returns the last error code of a PDFium function, which is just called.
// Usually, this function is called after a PDFium function returns, in order to check the error code of the previous PDFium function.
// Please note that when using go-pdfium from the same instance (on single-threaded any instance)
// from different subroutines, FPDF_GetLastError might already be reset from
// executing another PDFium method.
FPDF_GetLastError(request *requests.FPDF_GetLastError) (*responses.FPDF_GetLastError, error)

// FPDF_SetSandBoxPolicy set the policy for the sandbox environment.
Expand Down

0 comments on commit 7e1023a

Please sign in to comment.