@@ -3,14 +3,28 @@ package commonsteps
33import (
44 "context"
55 "fmt"
6-
6+ "log"
77 "net/http"
8+ "os"
9+ "path"
10+ "sort"
811
12+ "github.com/hashicorp/packer-plugin-sdk/didyoumean"
913 "github.com/hashicorp/packer-plugin-sdk/multistep"
1014 "github.com/hashicorp/packer-plugin-sdk/net"
1115 packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
1216)
1317
18+ func HTTPServerFromHTTPConfig (cfg * HTTPConfig ) * StepHTTPServer {
19+ return & StepHTTPServer {
20+ HTTPDir : cfg .HTTPDir ,
21+ HTTPContent : cfg .HTTPContent ,
22+ HTTPPortMin : cfg .HTTPPortMin ,
23+ HTTPPortMax : cfg .HTTPPortMax ,
24+ HTTPAddress : cfg .HTTPAddress ,
25+ }
26+ }
27+
1428// This step creates and runs the HTTP server that is serving files from the
1529// directory specified by the 'http_directory` configuration parameter in the
1630// template.
@@ -22,23 +36,66 @@ import (
2236// http_port int - The port the HTTP server started on.
2337type StepHTTPServer struct {
2438 HTTPDir string
39+ HTTPContent map [string ]string
2540 HTTPPortMin int
2641 HTTPPortMax int
2742 HTTPAddress string
2843
2944 l * net.Listener
3045}
3146
47+ func (s * StepHTTPServer ) Handler () http.Handler {
48+ if s .HTTPDir != "" {
49+ return http .FileServer (http .Dir (s .HTTPDir ))
50+ }
51+
52+ return MapServer (s .HTTPContent )
53+ }
54+
55+ type MapServer map [string ]string
56+
57+ func (s MapServer ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
58+ path := path .Clean (r .URL .Path )
59+ content , found := s [path ]
60+ if ! found {
61+ paths := make ([]string , 0 , len (s ))
62+ for k := range s {
63+ paths = append (paths , k )
64+ }
65+ sort .Strings (paths )
66+ err := fmt .Sprintf ("%s not found." , path )
67+ if sug := didyoumean .NameSuggestion (path , paths ); sug != "" {
68+ err += fmt .Sprintf (" Did you mean %q?" , sug )
69+ }
70+
71+ http .Error (w , err , http .StatusNotFound )
72+ return
73+ }
74+
75+ if _ , err := w .Write ([]byte (content )); err != nil {
76+ // log err in case the file couldn't be 100% transferred for example.
77+ log .Printf ("http_content serve error: %v" , err )
78+ }
79+ }
80+
3281func (s * StepHTTPServer ) Run (ctx context.Context , state multistep.StateBag ) multistep.StepAction {
3382 ui := state .Get ("ui" ).(packersdk.Ui )
3483
35- if s .HTTPDir == "" {
84+ if s .HTTPDir == "" && len ( s . HTTPContent ) == 0 {
3685 state .Put ("http_port" , 0 )
3786 return multistep .ActionContinue
3887 }
3988
89+ if s .HTTPDir != "" {
90+ if _ , err := os .Stat (s .HTTPDir ); err != nil {
91+ err := fmt .Errorf ("Error finding %q: %s" , s .HTTPDir , err )
92+ state .Put ("error" , err )
93+ ui .Error (err .Error ())
94+ return multistep .ActionHalt
95+ }
96+ }
97+
4098 // Find an available TCP port for our HTTP server
41- var httpAddr string
4299 var err error
43100 s .l , err = net.ListenRangeConfig {
44101 Min : s .HTTPPortMin ,
@@ -57,8 +114,7 @@ func (s *StepHTTPServer) Run(ctx context.Context, state multistep.StateBag) mult
57114 ui .Say (fmt .Sprintf ("Starting HTTP server on port %d" , s .l .Port ))
58115
59116 // Start the HTTP server and run it in the background
60- fileServer := http .FileServer (http .Dir (s .HTTPDir ))
61- server := & http.Server {Addr : httpAddr , Handler : fileServer }
117+ server := & http.Server {Addr : "" , Handler : s .Handler ()}
62118 go server .Serve (s .l )
63119
64120 // Save the address into the state so it can be accessed in the future
@@ -67,9 +123,20 @@ func (s *StepHTTPServer) Run(ctx context.Context, state multistep.StateBag) mult
67123 return multistep .ActionContinue
68124}
69125
70- func (s * StepHTTPServer ) Cleanup (multistep.StateBag ) {
126+ func (s * StepHTTPServer ) Cleanup (state multistep.StateBag ) {
71127 if s .l != nil {
128+ ui := state .Get ("ui" ).(packersdk.Ui )
129+
72130 // Close the listener so that the HTTP server stops
73- s .l .Close ()
131+ if err := s .l .Close (); err != nil {
132+ err = fmt .Errorf ("Failed closing http server on port %d: %w" , s .l .Port , err )
133+ ui .Error (err .Error ())
134+ // Here this error should be shown to the UI but it won't
135+ // specifically stop Packer from terminating successfully. It could
136+ // cause a "Listen leak" if it happenned a lot. Though Listen will
137+ // try other ports if one is already used. In the case we want to
138+ // Listen on only one port, the next Listen call could fail or be
139+ // longer than expected.
140+ }
74141 }
75142}
0 commit comments