@@ -19,12 +19,14 @@ import (
1919 "net/http"
2020 "net/url"
2121 "os"
22+ "path/filepath"
2223 "runtime"
2324 "strconv"
2425 "strings"
2526 "time"
2627
2728 "github.com/gohugoio/hugo/config"
29+
2830 "github.com/gohugoio/hugo/helpers"
2931 "github.com/spf13/afero"
3032 "github.com/spf13/cobra"
@@ -137,34 +139,58 @@ func server(cmd *cobra.Command, args []string) error {
137139 c .watchConfig ()
138140 }
139141
140- l , err := net .Listen ("tcp" , net .JoinHostPort (serverInterface , strconv .Itoa (serverPort )))
141- if err == nil {
142- l .Close ()
143- } else {
144- if serverCmd .Flags ().Changed ("port" ) {
145- // port set explicitly by user -- he/she probably meant it!
146- return newSystemErrorF ("Server startup failed: %s" , err )
147- }
148- jww .ERROR .Println ("port" , serverPort , "already in use, attempting to use an available port" )
149- sp , err := helpers .FindAvailablePort ()
150- if err != nil {
151- return newSystemError ("Unable to find alternative port to use:" , err )
142+ languages := c .languages ()
143+ serverPorts := make ([]int , 1 )
144+
145+ if languages .IsMultihost () {
146+ serverPorts = make ([]int , len (languages ))
147+ }
148+
149+ currentServerPort := serverPort
150+
151+ for i := 0 ; i < len (serverPorts ); i ++ {
152+ l , err := net .Listen ("tcp" , net .JoinHostPort (serverInterface , strconv .Itoa (currentServerPort )))
153+ if err == nil {
154+ l .Close ()
155+ serverPorts [i ] = currentServerPort
156+ } else {
157+ if i == 0 && serverCmd .Flags ().Changed ("port" ) {
158+ // port set explicitly by user -- he/she probably meant it!
159+ return newSystemErrorF ("Server startup failed: %s" , err )
160+ }
161+ jww .ERROR .Println ("port" , serverPort , "already in use, attempting to use an available port" )
162+ sp , err := helpers .FindAvailablePort ()
163+ if err != nil {
164+ return newSystemError ("Unable to find alternative port to use:" , err )
165+ }
166+ serverPorts [i ] = sp .Port
152167 }
153- serverPort = sp .Port
168+
169+ currentServerPort = serverPorts [i ] + 1
154170 }
155171
156172 c .Set ("port" , serverPort )
157173 if liveReloadPort != - 1 {
158174 c .Set ("liveReloadPort" , liveReloadPort )
159175 } else {
160- c .Set ("liveReloadPort" , serverPort )
176+ c .Set ("liveReloadPort" , serverPorts [ 0 ] )
161177 }
162178
163- baseURL , err = fixURL (c .Cfg , baseURL )
164- if err != nil {
165- return err
179+ if languages .IsMultihost () {
180+ for i , language := range languages {
181+ baseURL , err = fixURL (language , baseURL , serverPorts [i ])
182+ if err != nil {
183+ return err
184+ }
185+ language .Set ("baseURL" , baseURL )
186+ }
187+ } else {
188+ baseURL , err = fixURL (c .Cfg , baseURL , serverPorts [0 ])
189+ if err != nil {
190+ return err
191+ }
192+ c .Cfg .Set ("baseURL" , baseURL )
166193 }
167- c .Set ("baseURL" , baseURL )
168194
169195 if err := memStats (); err != nil {
170196 jww .ERROR .Println ("memstats error:" , err )
@@ -208,28 +234,52 @@ func server(cmd *cobra.Command, args []string) error {
208234 }
209235 }
210236
211- c .serve (serverPort )
212-
213237 return nil
214238}
215239
216- func (c * commandeer ) serve (port int ) {
240+ type fileServer struct {
241+ basePort int
242+ baseURLs []string
243+ roots []string
244+ c * commandeer
245+ }
246+
247+ func (f * fileServer ) createEndpoint (i int ) (* http.ServeMux , string , error ) {
248+ baseURL := f .baseURLs [i ]
249+ root := f .roots [i ]
250+ port := f .basePort + i
251+
252+ publishDir := f .c .Cfg .GetString ("publishDir" )
253+
254+ if root != "" {
255+ publishDir = filepath .Join (publishDir , root )
256+ }
257+
258+ absPublishDir := f .c .PathSpec ().AbsPathify (publishDir )
259+
260+ // TODO(bep) multihost unify feedback
217261 if renderToDisk {
218- jww .FEEDBACK .Println ("Serving pages from " + c . PathSpec (). AbsPathify ( c . Cfg . GetString ( "publishDir" )) )
262+ jww .FEEDBACK .Println ("Serving pages from " + absPublishDir )
219263 } else {
220264 jww .FEEDBACK .Println ("Serving pages from memory" )
221265 }
222266
223- httpFs := afero .NewHttpFs (c .Fs .Destination )
224- fs := filesOnlyFs {httpFs .Dir (c . PathSpec (). AbsPathify ( c . Cfg . GetString ( "publishDir" )) )}
267+ httpFs := afero .NewHttpFs (f . c .Fs .Destination )
268+ fs := filesOnlyFs {httpFs .Dir (absPublishDir )}
225269
226- doLiveReload := ! buildWatch && ! c .Cfg .GetBool ("disableLiveReload" )
227- fastRenderMode := doLiveReload && ! c .Cfg .GetBool ("disableFastRender" )
270+ doLiveReload := ! buildWatch && ! f . c .Cfg .GetBool ("disableLiveReload" )
271+ fastRenderMode := doLiveReload && ! f . c .Cfg .GetBool ("disableFastRender" )
228272
229273 if fastRenderMode {
230274 jww .FEEDBACK .Println ("Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender" )
231275 }
232276
277+ // We're only interested in the path
278+ u , err := url .Parse (baseURL )
279+ if err != nil {
280+ return nil , "" , fmt .Errorf ("Invalid baseURL: %s" , err )
281+ }
282+
233283 decorate := func (h http.Handler ) http.Handler {
234284 return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
235285 if noHTTPCache {
@@ -240,40 +290,86 @@ func (c *commandeer) serve(port int) {
240290 if fastRenderMode {
241291 p := r .RequestURI
242292 if strings .HasSuffix (p , "/" ) || strings .HasSuffix (p , "html" ) || strings .HasSuffix (p , "htm" ) {
243- c .visitedURLs .Add (p )
293+ f . c .visitedURLs .Add (p )
244294 }
245295 }
246296 h .ServeHTTP (w , r )
247297 })
248298 }
249299
250300 fileserver := decorate (http .FileServer (fs ))
301+ mu := http .NewServeMux ()
251302
252- // We're only interested in the path
253- u , err := url .Parse (c .Cfg .GetString ("baseURL" ))
254- if err != nil {
255- jww .ERROR .Fatalf ("Invalid baseURL: %s" , err )
256- }
257303 if u .Path == "" || u .Path == "/" {
258- http .Handle ("/" , fileserver )
304+ mu .Handle ("/" , fileserver )
259305 } else {
260- http .Handle (u .Path , http .StripPrefix (u .Path , fileserver ))
306+ mu .Handle (u .Path , http .StripPrefix (u .Path , fileserver ))
261307 }
262308
263- jww .FEEDBACK .Printf ("Web Server is available at %s (bind address %s)\n " , u .String (), serverInterface )
264- jww .FEEDBACK .Println ("Press Ctrl+C to stop" )
265-
266309 endpoint := net .JoinHostPort (serverInterface , strconv .Itoa (port ))
267- err = http .ListenAndServe (endpoint , nil )
268- if err != nil {
269- jww .ERROR .Printf ("Error: %s\n " , err .Error ())
270- os .Exit (1 )
310+
311+ return mu , endpoint , nil
312+ }
313+
314+ func (c * commandeer ) roots () []string {
315+ var roots []string
316+ languages := c .languages ()
317+ isMultiHost := languages .IsMultihost ()
318+ if ! isMultiHost {
319+ return roots
320+ }
321+
322+ for _ , l := range languages {
323+ roots = append (roots , l .Lang )
324+ }
325+ return roots
326+ }
327+
328+ func (c * commandeer ) serve (port int ) {
329+ // TODO(bep) multihost
330+ isMultiHost := Hugo .IsMultihost ()
331+
332+ var (
333+ baseURLs []string
334+ roots []string
335+ )
336+
337+ if isMultiHost {
338+ for _ , s := range Hugo .Sites {
339+ baseURLs = append (baseURLs , s .BaseURL .String ())
340+ roots = append (roots , s .Language .Lang )
341+ }
342+ } else {
343+ baseURLs = []string {Hugo .Sites [0 ].BaseURL .String ()}
344+ roots = []string {"" }
271345 }
346+
347+ srv := & fileServer {
348+ basePort : port ,
349+ baseURLs : baseURLs ,
350+ roots : roots ,
351+ c : c ,
352+ }
353+
354+ for i , _ := range baseURLs {
355+ mu , endpoint , err := srv .createEndpoint (i )
356+
357+ go func () {
358+ err = http .ListenAndServe (endpoint , mu )
359+ if err != nil {
360+ jww .ERROR .Printf ("Error: %s\n " , err .Error ())
361+ os .Exit (1 )
362+ }
363+ }()
364+ }
365+
366+ // TODO(bep) multihost jww.FEEDBACK.Printf("Web Server is available at %s (bind address %s)\n", u.String(), serverInterface)
367+ jww .FEEDBACK .Println ("Press Ctrl+C to stop" )
272368}
273369
274370// fixURL massages the baseURL into a form needed for serving
275371// all pages correctly.
276- func fixURL (cfg config.Provider , s string ) (string , error ) {
372+ func fixURL (cfg config.Provider , s string , port int ) (string , error ) {
277373 useLocalhost := false
278374 if s == "" {
279375 s = cfg .GetString ("baseURL" )
@@ -315,7 +411,7 @@ func fixURL(cfg config.Provider, s string) (string, error) {
315411 return "" , fmt .Errorf ("Failed to split baseURL hostpost: %s" , err )
316412 }
317413 }
318- u .Host += fmt .Sprintf (":%d" , serverPort )
414+ u .Host += fmt .Sprintf (":%d" , port )
319415 }
320416
321417 return u .String (), nil
0 commit comments