Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit eae70b7

Browse files
author
Bret Johnson
authored
Fix to handle app name specifications with slashes. The is the CLI ... (#471)
change to match the corresponding server change deployed a couple days back. Here are details of the issue: // IIS and Azure web apps have this annoying behavior where %2F (URL encoded slashes) in the URL are URL decoded // BEFORE the requests reach node. That essentially means there's no good way to encode a "/" in the app name-- // URL encodeing will work when running locally but when running on Azure it gets decoded before express sees it, // so app names with slashes don't get routed properly. See tjanczuk/iisnode#343 (or other sites // that complain about the same) for some more info. I explored some IIS config based workarounds, but the previous // link seems to say they won't work, so I eventually gave up on that. // Anyway, to workaround this issue, we now allow the client to encode / characters as ~~ (two tildes, URL encoded). // The CLI now converts / to ~~ if / appears in an app name, before passing that as part of the URL. This code below // does the encoding. It's hack, but seems like the least bad option here. // Eventually, this service will go away & we'll all be on Max's new service. That's hosted in docker, no more IIS, // so this issue should go away then.
1 parent 13092ee commit eae70b7

File tree

1 file changed

+34
-19
lines changed

1 file changed

+34
-19
lines changed

sdk/script/management-sdk.ts

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ class AccountManager {
211211
}
212212

213213
public getApp(appName: string): Promise<App> {
214-
return this.get(urlEncode `/apps/${appName}`)
214+
return this.get(urlEncode `/apps/${this.appNameParam(appName)}`)
215215
.then((res: JsonResponse) => res.body.app);
216216
}
217217

@@ -227,75 +227,75 @@ class AccountManager {
227227
}
228228

229229
public removeApp(appName: string): Promise<void> {
230-
return this.del(urlEncode `/apps/${appName}`)
230+
return this.del(urlEncode `/apps/${this.appNameParam(appName)}`)
231231
.then(() => null);
232232
}
233233

234234
public renameApp(oldAppName: string, newAppName: string): Promise<void> {
235-
return this.patch(urlEncode `/apps/${oldAppName}`, JSON.stringify({ name: newAppName }))
235+
return this.patch(urlEncode `/apps/${this.appNameParam(oldAppName)}`, JSON.stringify({ name: newAppName }))
236236
.then(() => null);
237237
}
238238

239239
public transferApp(appName: string, email: string): Promise<void> {
240-
return this.post(urlEncode `/apps/${appName}/transfer/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false)
240+
return this.post(urlEncode `/apps/${this.appNameParam(appName)}/transfer/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false)
241241
.then(() => null);
242242
}
243243

244244
// Collaborators
245245
public getCollaborators(appName: string): Promise<CollaboratorMap> {
246-
return this.get(urlEncode `/apps/${appName}/collaborators`)
246+
return this.get(urlEncode `/apps/${this.appNameParam(appName)}/collaborators`)
247247
.then((res: JsonResponse) => res.body.collaborators);
248248
}
249249

250250
public addCollaborator(appName: string, email: string): Promise<void> {
251-
return this.post(urlEncode `/apps/${appName}/collaborators/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false)
251+
return this.post(urlEncode `/apps/${this.appNameParam(appName)}/collaborators/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false)
252252
.then(() => null);
253253
}
254254

255255
public removeCollaborator(appName: string, email: string): Promise<void> {
256-
return this.del(urlEncode `/apps/${appName}/collaborators/${email}`)
256+
return this.del(urlEncode `/apps/${this.appNameParam(appName)}/collaborators/${email}`)
257257
.then(() => null);
258258
}
259259

260260
// Deployments
261261
public addDeployment(appName: string, deploymentName: string): Promise<Deployment> {
262262
var deployment = <Deployment>{ name: deploymentName };
263-
return this.post(urlEncode `/apps/${appName}/deployments/`, JSON.stringify(deployment), /*expectResponseBody=*/ true)
263+
return this.post(urlEncode `/apps/${this.appNameParam(appName)}/deployments/`, JSON.stringify(deployment), /*expectResponseBody=*/ true)
264264
.then((res: JsonResponse) => res.body.deployment);
265265
}
266266

267267
public clearDeploymentHistory(appName: string, deploymentName: string): Promise<void> {
268-
return this.del(urlEncode `/apps/${appName}/deployments/${deploymentName}/history`)
268+
return this.del(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/history`)
269269
.then(() => null);
270270
}
271271

272272
public getDeployments(appName: string): Promise<Deployment[]> {
273-
return this.get(urlEncode `/apps/${appName}/deployments/`)
273+
return this.get(urlEncode `/apps/${this.appNameParam(appName)}/deployments/`)
274274
.then((res: JsonResponse) => res.body.deployments);
275275
}
276276

277277
public getDeployment(appName: string, deploymentName: string): Promise<Deployment> {
278-
return this.get(urlEncode `/apps/${appName}/deployments/${deploymentName}`)
278+
return this.get(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}`)
279279
.then((res: JsonResponse) => res.body.deployment);
280280
}
281281

282282
public renameDeployment(appName: string, oldDeploymentName: string, newDeploymentName: string): Promise<void> {
283-
return this.patch(urlEncode `/apps/${appName}/deployments/${oldDeploymentName}`, JSON.stringify({ name: newDeploymentName }))
283+
return this.patch(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${oldDeploymentName}`, JSON.stringify({ name: newDeploymentName }))
284284
.then(() => null);
285285
}
286286

287287
public removeDeployment(appName: string, deploymentName: string): Promise<void> {
288-
return this.del(urlEncode `/apps/${appName}/deployments/${deploymentName}`)
288+
return this.del(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}`)
289289
.then(() => null);
290290
}
291291

292292
public getDeploymentMetrics(appName: string, deploymentName: string): Promise<DeploymentMetrics> {
293-
return this.get(urlEncode `/apps/${appName}/deployments/${deploymentName}/metrics`)
293+
return this.get(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/metrics`)
294294
.then((res: JsonResponse) => res.body.metrics);
295295
}
296296

297297
public getDeploymentHistory(appName: string, deploymentName: string): Promise<Package[]> {
298-
return this.get(urlEncode `/apps/${appName}/deployments/${deploymentName}/history`)
298+
return this.get(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/history`)
299299
.then((res: JsonResponse) => res.body.history);
300300
}
301301

@@ -304,7 +304,7 @@ class AccountManager {
304304
return Promise<void>((resolve, reject, notify) => {
305305

306306
updateMetadata.appVersion = targetBinaryVersion;
307-
var request: superagent.Request<any> = superagent.post(this._serverUrl + urlEncode `/apps/${appName}/deployments/${deploymentName}/release`);
307+
var request: superagent.Request<any> = superagent.post(this._serverUrl + urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/release`);
308308
if (this._proxy) (<any>request).proxy(this._proxy);
309309
this.attachCredentials(request);
310310

@@ -353,18 +353,18 @@ class AccountManager {
353353
public patchRelease(appName: string, deploymentName: string, label: string, updateMetadata: PackageInfo): Promise<void> {
354354
updateMetadata.label = label;
355355
var requestBody: string = JSON.stringify({ packageInfo: updateMetadata });
356-
return this.patch(urlEncode `/apps/${appName}/deployments/${deploymentName}/release`, requestBody, /*expectResponseBody=*/ false)
356+
return this.patch(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/release`, requestBody, /*expectResponseBody=*/ false)
357357
.then(() => null);
358358
}
359359

360360
public promote(appName: string, sourceDeploymentName: string, destinationDeploymentName: string, updateMetadata: PackageInfo): Promise<void> {
361361
var requestBody: string = JSON.stringify({ packageInfo: updateMetadata });
362-
return this.post(urlEncode `/apps/${appName}/deployments/${sourceDeploymentName}/promote/${destinationDeploymentName}`, requestBody, /*expectResponseBody=*/ false)
362+
return this.post(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${sourceDeploymentName}/promote/${destinationDeploymentName}`, requestBody, /*expectResponseBody=*/ false)
363363
.then(() => null);
364364
}
365365

366366
public rollback(appName: string, deploymentName: string, targetRelease?: string): Promise<void> {
367-
return this.post(urlEncode `/apps/${appName}/deployments/${deploymentName}/rollback/${targetRelease || ``}`, /*requestBody=*/ null, /*expectResponseBody=*/ false)
367+
return this.post(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/rollback/${targetRelease || ``}`, /*requestBody=*/ null, /*expectResponseBody=*/ false)
368368
.then(() => null);
369369
}
370370

@@ -516,6 +516,21 @@ class AccountManager {
516516
request.set("Authorization", `Bearer ${this._accessKey}`);
517517
request.set("X-CodePush-SDK-Version", packageJson.version);
518518
}
519+
520+
// IIS and Azure web apps have this annoying behavior where %2F (URL encoded slashes) in the URL are URL decoded
521+
// BEFORE the requests reach node. That essentially means there's no good way to encode a "/" in the app name--
522+
// URL encodeing will work when running locally but when running on Azure it gets decoded before express sees it,
523+
// so app names with slashes don't get routed properly. See https://github.com/tjanczuk/iisnode/issues/343 (or other sites
524+
// that complain about the same) for some more info. I explored some IIS config based workarounds, but the previous
525+
// link seems to say they won't work, so I eventually gave up on that.
526+
// Anyway, to workaround this issue, we now allow the client to encode / characters as ~~ (two tildes, URL encoded).
527+
// The CLI now converts / to ~~ if / appears in an app name, before passing that as part of the URL. This code below
528+
// does the encoding. It's hack, but seems like the least bad option here.
529+
// Eventually, this service will go away & we'll all be on Max's new service. That's hosted in docker, no more IIS,
530+
// so this issue should go away then.
531+
private appNameParam(appName: string) {
532+
return appName.replace("/", "~~");
533+
}
519534
}
520535

521536
export = AccountManager;

0 commit comments

Comments
 (0)