@@ -57,6 +57,7 @@ type API struct {
5757 vscsAPI string
5858 githubAPI string
5959 githubServer string
60+ retryBackoff time.Duration
6061}
6162
6263type httpClient interface {
@@ -79,6 +80,7 @@ func New(serverURL, apiURL, vscsURL string, httpClient httpClient) *API {
7980 vscsAPI : strings .TrimSuffix (vscsURL , "/" ),
8081 githubAPI : strings .TrimSuffix (apiURL , "/" ),
8182 githubServer : strings .TrimSuffix (serverURL , "/" ),
83+ retryBackoff : 100 * time .Millisecond ,
8284 }
8385}
8486
@@ -301,24 +303,24 @@ func findNextPage(linkValue string) string {
301303// If the codespace is not found, an error is returned.
302304// If includeConnection is true, it will return the connection information for the codespace.
303305func (a * API ) GetCodespace (ctx context.Context , codespaceName string , includeConnection bool ) (* Codespace , error ) {
304- req , err := http . NewRequest (
305- http .MethodGet ,
306- a . githubAPI + "/user/codespaces/" + codespaceName ,
307- nil ,
308- )
309- if err != nil {
310- return nil , fmt . Errorf ( "error creating request: %w" , err )
311- }
312-
313- if includeConnection {
314- q := req .URL .Query ()
315- q .Add ("internal" , "true" )
316- q .Add ("refresh" , "true" )
317- req .URL .RawQuery = q .Encode ()
318- }
319-
320- a . setHeaders ( req )
321- resp , err := a . do ( ctx , req , "/user/codespaces/*" )
306+ resp , err := a . withRetry ( func () ( * http. Response , error ) {
307+ req , err := http .NewRequest (
308+ http . MethodGet ,
309+ a . githubAPI + "/user/codespaces/" + codespaceName ,
310+ nil ,
311+ )
312+ if err != nil {
313+ return nil , fmt . Errorf ( "error creating request: %w" , err )
314+ }
315+ if includeConnection {
316+ q := req .URL .Query ()
317+ q .Add ("internal" , "true" )
318+ q .Add ("refresh" , "true" )
319+ req .URL .RawQuery = q .Encode ()
320+ }
321+ a . setHeaders ( req )
322+ return a . do ( ctx , req , "/user/codespaces/*" )
323+ } )
322324 if err != nil {
323325 return nil , fmt .Errorf ("error making request: %w" , err )
324326 }
@@ -344,17 +346,18 @@ func (a *API) GetCodespace(ctx context.Context, codespaceName string, includeCon
344346// StartCodespace starts a codespace for the user.
345347// If the codespace is already running, the returned error from the API is ignored.
346348func (a * API ) StartCodespace (ctx context.Context , codespaceName string ) error {
347- req , err := http .NewRequest (
348- http .MethodPost ,
349- a .githubAPI + "/user/codespaces/" + codespaceName + "/start" ,
350- nil ,
351- )
352- if err != nil {
353- return fmt .Errorf ("error creating request: %w" , err )
354- }
355-
356- a .setHeaders (req )
357- resp , err := a .do (ctx , req , "/user/codespaces/*/start" )
349+ resp , err := a .withRetry (func () (* http.Response , error ) {
350+ req , err := http .NewRequest (
351+ http .MethodPost ,
352+ a .githubAPI + "/user/codespaces/" + codespaceName + "/start" ,
353+ nil ,
354+ )
355+ if err != nil {
356+ return nil , fmt .Errorf ("error creating request: %w" , err )
357+ }
358+ a .setHeaders (req )
359+ return a .do (ctx , req , "/user/codespaces/*/start" )
360+ })
358361 if err != nil {
359362 return fmt .Errorf ("error making request: %w" , err )
360363 }
@@ -686,3 +689,19 @@ func (a *API) do(ctx context.Context, req *http.Request, spanName string) (*http
686689func (a * API ) setHeaders (req * http.Request ) {
687690 req .Header .Set ("Accept" , "application/vnd.github.v3+json" )
688691}
692+
693+ // withRetry takes a generic function that sends an http request and retries
694+ // only when the returned response has a >=500 status code.
695+ func (a * API ) withRetry (f func () (* http.Response , error )) (resp * http.Response , err error ) {
696+ for i := 0 ; i < 5 ; i ++ {
697+ resp , err = f ()
698+ if err != nil {
699+ return nil , err
700+ }
701+ if resp .StatusCode < 500 {
702+ break
703+ }
704+ time .Sleep (a .retryBackoff * (time .Duration (i ) + 1 ))
705+ }
706+ return resp , err
707+ }
0 commit comments