@@ -73,15 +73,12 @@ function ACME.create(conf)
7373 local resp
7474 local err
7575
76- for _ , d in ipairs ({" /directory" , " /dir" , " /" }) do
77- resp , err = http .get {url = config .ca .proxy_uri .. d }
78-
79- if resp and resp .status_code == 200 and resp .headers [" content-type" ]
80- and resp .headers [" content-type" ]:match (" application/json" ) then
81- self .resources = json .decode (resp .content )
82- self .nonce = resp .headers [" replay-nonce" ]
83- break
84- end
76+ resp , err = self :get {url = config .ca .proxy_uri .. " /directory" }
77+
78+ if resp and resp .status_code == 200 and resp .headers [" content-type" ]
79+ and resp .headers [" content-type" ]:match (" application/json" ) then
80+ self .resources = json .decode (resp .content )
81+ self .nonce = resp .headers [" replay-nonce" ]
8582 end
8683
8784 if not self .resources then
@@ -103,14 +100,31 @@ end
103100
104101--- Adapt resource URLs when going through HAProxy
105102function ACME .proxy_url (self , url )
106-
107103 if url :sub (1 , # self .conf .ca .uri ) == self .conf .ca .uri then
108104 return string.format (" %s%s" , self .conf .ca .proxy_uri , url :sub (# self .conf .ca .uri ))
109105 else
110106 return url
111107 end
112108end
113109
110+ --- ACME wrapper for http.get()
111+ --
112+ -- @param resource ACME resource type
113+ -- @param url Valid HTTP url (mandatory)G
114+ --
115+ -- @return Response object or tuple (nil, msg) on errors
116+ function ACME .get (self , t )
117+ local resp , err = http .get {url = self :proxy_url (t .url )}
118+
119+ if (not t .retry or t .retry < 5 ) and (not resp or (resp and resp .status_code == 503 )) then
120+ t .retry = not t .retry and 1 or t .retry + 1
121+
122+ return self :get (t )
123+ end
124+
125+ return resp , err
126+ end
127+
114128--- ACME wrapper for http.post()
115129--
116130-- @param resource ACME resource type
@@ -136,6 +150,7 @@ function ACME.post(self, t)
136150
137151 local resp , err = http .post {url = self :proxy_url (t .url ), data = jws ,
138152 headers = t .headers , timeout = t .timeout }
153+
139154 if resp and resp .headers then
140155 self .nonce = resp .headers [" replay-nonce" ]
141156
@@ -151,12 +166,74 @@ function ACME.post(self, t)
151166 return nil , err
152167 end
153168
154- resp , err = http . post {url = self :proxy_url (t .url ), data = jws ,
169+ resp , err = self : post {url = self :proxy_url (t .url ), data = jws ,
155170 headers = t .headers }
156171 end
157172 end
158173 end
159174
175+ if (not t .retry or t .retry < 5 ) and (not resp or (resp and resp .status_code == 503 )) then
176+ t .retry = not t .retry and 1 or t .retry + 1
177+
178+ return self :post (t )
179+ end
180+
181+ return resp , err
182+ end
183+
184+ --- ACME wrapper for POST-as-GET
185+ --
186+ -- @param resource ACME resource type
187+ -- @param url Valid HTTP url (mandatory)G
188+ -- @param headers Lua table with request headers
189+ -- @param data Request content
190+ --
191+ -- @return Response object or tuple (nil, msg) on errors
192+ function ACME .postAsGet (self , t )
193+ local jws , err = self :jws {url = t .url , payload = nil }
194+
195+ if not jws then
196+ return nil , err
197+ end
198+
199+ if not t .headers then
200+ t .headers = {
201+ [" content-type" ] = " application/jose+json"
202+ }
203+ elseif not t .headers [" content-type" ] then
204+ t .headers [" content-type" ] = " application/jose+json"
205+ end
206+
207+ local resp , err = http .post {url = self :proxy_url (t .url ), data = jws ,
208+ headers = t .headers , timeout = t .timeout }
209+
210+ if resp and resp .headers then
211+ self .nonce = resp .headers [" replay-nonce" ]
212+
213+ if resp .status_code == 400 then
214+ local info = resp :json ()
215+
216+ if info and info .type == " urn:ietf:params:acme:error:badNonce" then
217+
218+ -- We need to retry once more with new nonce (hence new jws)
219+ jws , err = self :jws {resource = t .resource , url = t .url ,
220+ payload = " " }
221+ if not jws then
222+ return nil , err
223+ end
224+
225+ resp , err = self :post {url = self :proxy_url (t .url ), data = jws ,
226+ headers = t .headers }
227+ end
228+ end
229+ end
230+
231+ if (not t .retry or t .retry < 5 ) and (not resp or (resp and resp .status_code == 503 )) then
232+ t .retry = not t .retry and 1 or t .retry + 1
233+
234+ return self :postAsGet (t )
235+ end
236+
160237 return resp , err
161238end
162239
@@ -171,14 +248,13 @@ function ACME.refresh_nonce(self)
171248 self .nonce = nil
172249 if nonce then return nonce end
173250
251+ local resp , e = http .head {url = self :proxy_url (self .resources [" newNonce" ])}
174252
175- local r , e = http .head {url = self :proxy_url (self .resources [" newNonce" ])}
176-
177- if r and r .headers then
253+ if resp and resp .headers then
178254 -- TODO: Expect status code 204
179255 -- TODO: Expect Cache-Control: no-store
180256 -- TODO: Expect content size 0
181- return r .headers [" replay-nonce" ]
257+ return resp .headers [" replay-nonce" ]
182258 else
183259 return nil , e
184260 end
@@ -194,9 +270,9 @@ function ACME.jws(self, t)
194270 return nil , " ACME.jws: Account key does not exist."
195271 end
196272
197- if not t or not t .resource or not t . url or not t . payload then
273+ if not t or not t .url then
198274 return nil ,
199- " ACME.jws: Missing one or more parameters (resource, url, payload )"
275+ " ACME.jws: Missing one or more parameters (url)"
200276 end
201277
202278 -- if key:type() == rsaEncryption
@@ -233,7 +309,7 @@ function ACME.jws(self, t)
233309 end
234310
235311 jws .protected = http .base64 .encode (json .encode (jws .protected ), base64enc )
236- jws .payload = http .base64 .encode (json .encode (t .payload ), base64enc )
312+ jws .payload = t . payload and http .base64 .encode (json .encode (t .payload ), base64enc ) or " "
237313 local digest = openssl .digest .new (" SHA256" )
238314 digest :update (jws .protected .. " ." .. jws .payload )
239315 jws .signature = http .base64 .encode (self .account .key :sign (digest ), base64enc )
@@ -341,7 +417,7 @@ local function new_order(applet)
341417 }
342418
343419 -- Get auth token
344- local auth , err = http . get {url = acme : proxy_url ( auth ) }
420+ local auth , err = acme : postAsGet {url = auth }
345421
346422 if auth then
347423 local auth_json = auth :json ()
@@ -351,7 +427,7 @@ local function new_order(applet)
351427 http_challenges [ch .token ] = string.format (" %s.%s" ,
352428 ch .token , acme .account .thumbprint )
353429 resp , err = acme :post {url = ch .url , data = ch ,
354- resource = " challengeDone" , timeout = 1 }
430+ resource = " challengeDone" }
355431 challenge_token = ch .token
356432 break
357433 end
@@ -363,7 +439,8 @@ local function new_order(applet)
363439 local order_status
364440 for _ , t in pairs ({1 , 1 , 2 , 3 , 5 , 8 , 13 }) do
365441 core .sleep (t )
366- local resp , err = http .get {url = acme :proxy_url (order .headers [" location" ])}
442+ local resp , err = acme :postAsGet {url = order .headers [" location" ]}
443+
367444 if resp then
368445 order_status = resp :json ()
369446 if order_status .status == " ready" then
@@ -424,7 +501,7 @@ local function new_order(applet)
424501 return http .response .create {status_code = 500 , content = " No cert" }:send (applet )
425502 end
426503
427- local resp , err = http . get {url = acme : proxy_url ( resp_json .certificate ) }
504+ local resp , err = acme : postAsGet {url = resp_json .certificate }
428505 local bundle = string.format (" %s%s" , resp .content , key :toPEM (" private" ))
429506 return http .response .create {status_code = 200 , content = bundle }:send (applet )
430507 else
@@ -476,4 +553,4 @@ local function main(applet)
476553 end
477554end
478555
479- core .register_service (" acme" , " http" , main )
556+ core .register_service (" acme" , " http" , main )
0 commit comments