Skip to content

Commit

Permalink
Rewrite server-relevant API. Update document.
Browse files Browse the repository at this point in the history
  • Loading branch information
yangziy committed Sep 17, 2018
1 parent 11e05fc commit 6ca2130
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 50 deletions.
134 changes: 103 additions & 31 deletions HTTPAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
* Check current status (on/off)

- Toggle the client

- Get server list

- Switch server

- Get current server
- Select server
- Add new / modify existing server
- Delete server
- Get current mode

- Switch mode

# Specification
Expand All @@ -26,60 +25,124 @@ URL: http://localhost:9528/
}
```

- #### Toggle the client `POST /toggle`
- #### Toggle the client `POST /status`

###### Sample Return

```
{
"status": 1
// 1 for toggle succeed, 0 for fail
}
```

- #### Get server list `GET /servers`
`1` for success, `0` for failure.

- #### Get server list `GET /server/list`

###### Sample Return

```
[
{
"active": 1,
"id": "93C547E0-49C9-1234-9CAD-EE8D5C4A1A8F",
"remark": "us1",
// remark: as in Server Preferences Panel of the app.
},
{
"active" : 0,
"id" : "71552DCD-B298-495E-904E-82DA4B07AEF8",
"remark" : "hk2"
},
{
"active" : 0,
"id" : "E8879F3D-95AE-4714-BC04-9B271C2BC52D",
"remark" : "jp1"
},...
{
"Id" : "93C127E0-49C9-4332-9CAD-EE6B9A3D1A8F",
"Method" : "chacha20-ietf-poly1305",
"Password" : "password",
"Plugin" : "",
"PluginOptions" : "",
"Remark" : "jp1",
"ServerHost" : "jp1-sta40.somehost.com",
"ServerPort" : 49234
},
{
"Id" : "71552DCD-B298-4591-B59A-82DA4B07AEF8",
"Method" : "chacha20-ietf-poly1305",
"Password" : "password",
"Plugin" : "",
"PluginOptions" : "",
"Remark" : "us1",
"ServerHost" : "us1-sta40.somehost.com",
"ServerPort" : 49234
},...
]
```

- #### Switch server `POST /servers`
- #### Get current server `GET /server/current`

###### Sample Return

```
{
"Id" : "93C127E0-49C9-4332-9CAD-EE6B9A3D1A8F"
}
```

- #### Select server `POST /server/current`

###### Argument

| Name | Description | Sample |
| ---- | ----------------------------- | -------------------------------------- |
| id | As returned in `GET /servers` | "E8879F3D-95AE-4714-BC04-9B271C2BC52D" |
| Name | Description | Sample |
| ---- | --------------------------------- | -------------------------------------- |
| Id | As returned in `GET /server/list` | "71552DCD-B298-4591-B59A-82DA4B07AEF8" |

###### Sample Return

```
{
"status": 1
// 1 for succeed, 0 for fail
}
```

If the `id` is invalid or fail to match any `id` in config, "status" = 0.
If the `Id` is invalid or fail to match any id in config, `"status": 0`.

- #### Add Server / Modify Existing Server `POST /server `

###### Argument

| Name | Sample |
| ------------- | ---------------------- |
| ServerPort | 49234 |
| ServerHost | jp1-sta40.somehost.com |
| Remark | jp1 |
| PluginOptions | |
| Plugin | |
| Password | Password |
| Method | chacha20-ietf-poly1305 |

To indicate modification, pass `Id` in addition.

| Name | Description | Sample |
| ---- | --------------------------------- | -------------------------------------- |
| Id | As returned in `GET /server/list` | "71552DCD-B298-4591-B59A-82DA4B07AEF8" |

For meaning of the arguments, refer to `GET /server/list` and the Server Perferences Panel of the app.

###### Sample Return

```
{
"status": 1
}
```

- #### Delete Server `DELETE /server`

###### Argument

| Name | Description | Sample |
| ---- | --------------------------------- | -------------------------------------- |
| Id | As returned in `GET /server/list` | "71552DCD-B298-4591-B59A-82DA4B07AEF8" |

###### Sample Return

```
{
"status": 1
}
```

If `Id` == id of current server, operation will no effect, `"status":0`.

If `Id` not match, `"status":0`.

- #### Get current mode `GET /mode`

Expand All @@ -95,6 +158,12 @@ If the `id` is invalid or fail to match any `id` in config, "status" = 0.

- #### Switch mode `POST /mode`

###### Argument

| Name | Description | Sample |
| ---- | -------------------------- | -------- |
| mode | As returned in `GET /mode` | "global" |

###### Sample Return

```
Expand All @@ -104,4 +173,7 @@ If the `id` is invalid or fail to match any `id` in config, "status" = 0.
}
```

If the `mode`∉ {"auto", "global", "manual"}, "status" = 0.
---

All json names are case sensitive. Be careful.

72 changes: 64 additions & 8 deletions ShadowsocksX-NG/HTTPUserProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,36 +35,92 @@ class HTTPUserProxy{
return GCDWebServerDataResponse(jsonObject: ["enable":isOn], contentType: "json")
})

apiserver.addHandler(forMethod: "POST", path: "/toggle", request: GCDWebServerRequest.self, processBlock: {request in
apiserver.addHandler(forMethod: "POST", path: "/status", request: GCDWebServerURLEncodedFormRequest.self, processBlock: {request in
self.appdeleget.doToggleRunning(showToast: false)
return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")
})

apiserver.addHandler(forMethod: "GET", path: "/servers", request: GCDWebServerRequest.self, processBlock: {request in
apiserver.addHandler(forMethod: "GET", path: "/server/list", request: GCDWebServerRequest.self, processBlock: {request in

var data = [[String:Any]]()

for each in self.SerMgr.profiles{
data.append(["id":each.uuid,"remark":each.remark,
"active":self.SerMgr.activeProfileId == each.uuid ? 1 : 0])
data.append(each.toDictionary())
}

return GCDWebServerDataResponse(jsonObject: data, contentType: "json")
})

apiserver.addHandler(forMethod: "POST", path: "/servers", request: GCDWebServerURLEncodedFormRequest.self, processBlock: {request in
apiserver.addHandler(forMethod: "GET", path: "/server/current", request: GCDWebServerRequest.self, processBlock: {request in

let uuid = ((request as! GCDWebServerURLEncodedFormRequest).arguments["id"])as? String
return GCDWebServerDataResponse(jsonObject: ["Id":self.SerMgr.activeProfileId], contentType: "json")
})

apiserver.addHandler(forMethod: "POST", path: "/server/current", request: GCDWebServerURLEncodedFormRequest.self, processBlock: {request in

let uuid = ((request as! GCDWebServerURLEncodedFormRequest).arguments["Id"])as? String
for each in self.SerMgr.profiles{
if (each.uuid == uuid) {
self.appdeleget.changeServer(uuid: uuid!)
return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")


}
}
return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
})

apiserver.addHandler(forMethod: "POST", path: "/server", request: GCDWebServerURLEncodedFormRequest.self, processBlock: {request in

var data = ((request as! GCDWebServerURLEncodedFormRequest).arguments) as! [String: Any]
data["ServerPort"] = Double(data["ServerPort"] as! String)
let id = data["Id"] as? String
if (id != nil) {
for each in self.SerMgr.profiles{
if (each.uuid == id) {
ServerProfile.copy(fromDict: data, toProfile: each)
if (each.isValid()) {
self.SerMgr.save()
self.appdeleget.updateServersMenu()
return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")
}
}
}
}
else {
let profile = ServerProfile.fromDictionary(data)
if (profile.isValid()) {
self.SerMgr.profiles.append(profile)
self.SerMgr.save()
self.appdeleget.updateServersMenu()
return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")
}
}

return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
})

apiserver.addHandler(forMethod: "DELETE", path: "/server", request: GCDWebServerRequest.self
, processBlock: {request in

let uuid = (request.query?["Id"])as! String

if (uuid == self.SerMgr.activeProfileId) {
return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
}

for i in 0..<self.SerMgr.profiles.count{
if (self.SerMgr.profiles[i].uuid == uuid) {
self.SerMgr.profiles.remove(at: i)

self.SerMgr.save()
self.appdeleget.updateServersMenu()

return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")
}
}

return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
})

apiserver.addHandler(forMethod: "GET", path: "/mode", request: GCDWebServerRequest.self, processBlock: {request in
if let current = self.defaults.string(forKey: "ShadowsocksRunningMode"){
Expand All @@ -79,7 +135,7 @@ class HTTPUserProxy{
if (arg != "auto" && arg != "global" && arg != "manual") {
return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
}

self.appdeleget.changeMode(mode: arg!)

return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")
Expand Down
25 changes: 14 additions & 11 deletions ShadowsocksX-NG/ServerProfile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,31 +122,34 @@ class ServerProfile: NSObject, NSCopying {
return copy;
}

static func fromDictionary(_ data:[String:Any?]) -> ServerProfile {
static func copy(fromDict:[String:Any?], toProfile:ServerProfile) {
let cp = {
(profile: ServerProfile) in
profile.serverHost = data["ServerHost"] as! String
profile.serverPort = (data["ServerPort"] as! NSNumber).uint16Value
profile.method = data["Method"] as! String
profile.password = data["Password"] as! String
if let remark = data["Remark"] {
profile.serverHost = fromDict["ServerHost"] as! String
profile.serverPort = (fromDict["ServerPort"] as! NSNumber).uint16Value
profile.method = fromDict["Method"] as! String
profile.password = fromDict["Password"] as! String
if let remark = fromDict["Remark"] {
profile.remark = remark as! String
}
if let plugin = data["Plugin"] as? String {
if let plugin = fromDict["Plugin"] as? String {
profile.plugin = plugin
}
if let pluginOptions = data["PluginOptions"] as? String {
if let pluginOptions = fromDict["PluginOptions"] as? String {
profile.pluginOptions = pluginOptions
}
}

cp(toProfile)
}

static func fromDictionary(_ data:[String:Any?]) -> ServerProfile {
if let id = data["Id"] as? String {
let profile = ServerProfile(uuid: id)
cp(profile)
copy(fromDict: data, toProfile: profile)
return profile
} else {
let profile = ServerProfile()
cp(profile)
copy(fromDict: data, toProfile: profile)
return profile
}
}
Expand Down

0 comments on commit 6ca2130

Please sign in to comment.