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 }