github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/lcpserver/server/server.go (about)

     1  // Copyright (c) 2016 Readium Foundation
     2  //
     3  // Redistribution and use in source and binary forms, with or without modification,
     4  // are permitted provided that the following conditions are met:
     5  //
     6  // 1. Redistributions of source code must retain the above copyright notice, this
     7  //    list of conditions and the following disclaimer.
     8  // 2. Redistributions in binary form must reproduce the above copyright notice,
     9  //    this list of conditions and the following disclaimer in the documentation and/or
    10  //    other materials provided with the distribution.
    11  // 3. Neither the name of the organization nor the names of its contributors may be
    12  //    used to endorse or promote products derived from this software without specific
    13  //    prior written permission
    14  //
    15  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    16  // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    17  // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    18  // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    19  // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    20  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    21  // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    22  // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    23  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    24  // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    25  
    26  package lcpserver
    27  
    28  import (
    29  	"crypto/tls"
    30  	"net/http"
    31  	"time"
    32  
    33  	auth "github.com/abbot/go-http-auth"
    34  	"github.com/gorilla/mux"
    35  
    36  	"github.com/readium/readium-lcp-server/api"
    37  	"github.com/readium/readium-lcp-server/config"
    38  	"github.com/readium/readium-lcp-server/index"
    39  	apilcp "github.com/readium/readium-lcp-server/lcpserver/api"
    40  	"github.com/readium/readium-lcp-server/license"
    41  	"github.com/readium/readium-lcp-server/pack"
    42  	"github.com/readium/readium-lcp-server/storage"
    43  )
    44  
    45  type Server struct {
    46  	http.Server
    47  	readonly bool
    48  	idx      *index.Index
    49  	st       *storage.Store
    50  	lst      *license.Store
    51  	cert     *tls.Certificate
    52  	source   pack.ManualSource
    53  }
    54  
    55  func (s *Server) Store() storage.Store {
    56  	return *s.st
    57  }
    58  
    59  func (s *Server) Index() index.Index {
    60  	return *s.idx
    61  }
    62  
    63  func (s *Server) Licenses() license.Store {
    64  	return *s.lst
    65  }
    66  
    67  func (s *Server) Certificate() *tls.Certificate {
    68  	return s.cert
    69  }
    70  
    71  func (s *Server) Source() *pack.ManualSource {
    72  	return &s.source
    73  }
    74  
    75  func New(bindAddr string, readonly bool, idx *index.Index, st *storage.Store, lst *license.Store, cert *tls.Certificate, packager *pack.Packager, basicAuth *auth.BasicAuth) *Server {
    76  
    77  	sr := api.CreateServerRouter("")
    78  
    79  	s := &Server{
    80  		Server: http.Server{
    81  			Handler:        sr.N,
    82  			Addr:           bindAddr,
    83  			WriteTimeout:   240 * time.Second,
    84  			ReadTimeout:    15 * time.Second,
    85  			MaxHeaderBytes: 1 << 20,
    86  		},
    87  		readonly: readonly,
    88  		idx:      idx,
    89  		st:       st,
    90  		lst:      lst,
    91  		cert:     cert,
    92  		source:   pack.ManualSource{},
    93  	}
    94  
    95  	// Route.PathPrefix: http://www.gorillatoolkit.org/pkg/mux#Route.PathPrefix
    96  	// Route.Subrouter: http://www.gorillatoolkit.org/pkg/mux#Route.Subrouter
    97  	// Router.StrictSlash: http://www.gorillatoolkit.org/pkg/mux#Router.StrictSlash
    98  
    99  	// Serve static resources from a configurable directory.
   100  	// This is used when lcpencrypt sends encrypted resources and cover images to an fs storage,
   101  	// and we want this http server to provide such resources to the outside world (e.g. PubStore).
   102  	resourceDir := config.Config.LcpServer.Resources
   103  	sr.R.PathPrefix("/resources/").Handler(http.StripPrefix("/resources/", http.FileServer(http.Dir(resourceDir))))
   104  
   105  	// Methods related to encrypted content
   106  
   107  	contentRoutesPathPrefix := "/contents"
   108  	contentRoutes := sr.R.PathPrefix(contentRoutesPathPrefix).Subrouter().StrictSlash(false)
   109  
   110  	s.handleFunc(sr.R, contentRoutesPathPrefix, apilcp.ListContents).Methods("GET")
   111  
   112  	// get encrypted content by content id (a uuid)
   113  	s.handleFunc(contentRoutes, "/{content_id}", apilcp.GetContent).Methods("GET")
   114  	// get all licenses associated with a given content
   115  	s.handlePrivateFunc(contentRoutes, "/{content_id}/licenses", apilcp.ListLicensesForContent, basicAuth).Methods("GET")
   116  
   117  	if !readonly {
   118  		// create a publication
   119  		s.handlePrivateFunc(contentRoutes, "/{content_id}", apilcp.AddContent, basicAuth).Methods("PUT")
   120  		// delete a publication
   121  		s.handlePrivateFunc(contentRoutes, "/{content_id}", apilcp.DeleteContent, basicAuth).Methods("DELETE")
   122  		// generate a license for given content
   123  		s.handlePrivateFunc(contentRoutes, "/{content_id}/license", apilcp.GenerateLicense, basicAuth).Methods("POST")
   124  		// deprecated, from a typo in the lcp server spec
   125  		s.handlePrivateFunc(contentRoutes, "/{content_id}/licenses", apilcp.GenerateLicense, basicAuth).Methods("POST")
   126  		// generate a licensed publication
   127  		s.handlePrivateFunc(contentRoutes, "/{content_id}/publication", apilcp.GenerateLicensedPublication, basicAuth).Methods("POST")
   128  		// deprecated, from a typo in the lcp server spec
   129  		s.handlePrivateFunc(contentRoutes, "/{content_id}/publications", apilcp.GenerateLicensedPublication, basicAuth).Methods("POST")
   130  	}
   131  
   132  	// Methods related to licenses
   133  
   134  	licenseRoutesPathPrefix := "/licenses"
   135  	licenseRoutes := sr.R.PathPrefix(licenseRoutesPathPrefix).Subrouter().StrictSlash(false)
   136  
   137  	s.handlePrivateFunc(sr.R, licenseRoutesPathPrefix, apilcp.ListLicenses, basicAuth).Methods("GET")
   138  	// get a license
   139  	s.handlePrivateFunc(licenseRoutes, "/{license_id}", apilcp.GetLicense, basicAuth).Methods("GET")
   140  	s.handlePrivateFunc(licenseRoutes, "/{license_id}", apilcp.GetLicense, basicAuth).Methods("POST")
   141  	// get a licensed publication via a license id
   142  	s.handlePrivateFunc(licenseRoutes, "/{license_id}/publication", apilcp.GetLicensedPublication, basicAuth).Methods("POST")
   143  	if !readonly {
   144  		// update a license
   145  		s.handlePrivateFunc(licenseRoutes, "/{license_id}", apilcp.UpdateLicense, basicAuth).Methods("PATCH")
   146  	}
   147  
   148  	s.source.Feed(packager.Incoming)
   149  	return s
   150  }
   151  
   152  type HandlerFunc func(w http.ResponseWriter, r *http.Request, s apilcp.Server)
   153  
   154  func (s *Server) handleFunc(router *mux.Router, route string, fn HandlerFunc) *mux.Route {
   155  	return router.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
   156  		fn(w, r, s)
   157  	})
   158  }
   159  
   160  type HandlerPrivateFunc func(w http.ResponseWriter, r *auth.AuthenticatedRequest, s apilcp.Server)
   161  
   162  func (s *Server) handlePrivateFunc(router *mux.Router, route string, fn HandlerFunc, authenticator *auth.BasicAuth) *mux.Route {
   163  	return router.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
   164  		if api.CheckAuth(authenticator, w, r) {
   165  			fn(w, r, s)
   166  		}
   167  	})
   168  }