Skip to content

Commit 6ca2130

Browse files
committed
Rewrite server-relevant API. Update document.
1 parent 11e05fc commit 6ca2130

File tree

3 files changed

+181
-50
lines changed

3 files changed

+181
-50
lines changed

HTTPAPI.md

Lines changed: 103 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
* Check current status (on/off)
44

55
- Toggle the client
6-
76
- Get server list
8-
9-
- Switch server
10-
7+
- Get current server
8+
- Select server
9+
- Add new / modify existing server
10+
- Delete server
1111
- Get current mode
12-
1312
- Switch mode
1413

1514
# Specification
@@ -26,60 +25,124 @@ URL: http://localhost:9528/
2625
}
2726
```
2827

29-
- #### Toggle the client `POST /toggle`
28+
- #### Toggle the client `POST /status`
3029

3130
###### Sample Return
3231

3332
```
3433
{
3534
"status": 1
36-
// 1 for toggle succeed, 0 for fail
3735
}
3836
```
3937

40-
- #### Get server list `GET /servers`
38+
`1` for success, `0` for failure.
39+
40+
- #### Get server list `GET /server/list`
4141

4242
###### Sample Return
4343

4444
```
4545
[
46-
{
47-
"active": 1,
48-
"id": "93C547E0-49C9-1234-9CAD-EE8D5C4A1A8F",
49-
"remark": "us1",
50-
// remark: as in Server Preferences Panel of the app.
51-
},
52-
{
53-
"active" : 0,
54-
"id" : "71552DCD-B298-495E-904E-82DA4B07AEF8",
55-
"remark" : "hk2"
56-
},
57-
{
58-
"active" : 0,
59-
"id" : "E8879F3D-95AE-4714-BC04-9B271C2BC52D",
60-
"remark" : "jp1"
61-
},...
46+
{
47+
"Id" : "93C127E0-49C9-4332-9CAD-EE6B9A3D1A8F",
48+
"Method" : "chacha20-ietf-poly1305",
49+
"Password" : "password",
50+
"Plugin" : "",
51+
"PluginOptions" : "",
52+
"Remark" : "jp1",
53+
"ServerHost" : "jp1-sta40.somehost.com",
54+
"ServerPort" : 49234
55+
},
56+
{
57+
"Id" : "71552DCD-B298-4591-B59A-82DA4B07AEF8",
58+
"Method" : "chacha20-ietf-poly1305",
59+
"Password" : "password",
60+
"Plugin" : "",
61+
"PluginOptions" : "",
62+
"Remark" : "us1",
63+
"ServerHost" : "us1-sta40.somehost.com",
64+
"ServerPort" : 49234
65+
},...
6266
]
6367
```
6468

65-
- #### Switch server `POST /servers`
69+
- #### Get current server `GET /server/current`
70+
71+
###### Sample Return
72+
73+
```
74+
{
75+
"Id" : "93C127E0-49C9-4332-9CAD-EE6B9A3D1A8F"
76+
}
77+
```
78+
79+
- #### Select server `POST /server/current`
6680

6781
###### Argument
6882

69-
| Name | Description | Sample |
70-
| ---- | ----------------------------- | -------------------------------------- |
71-
| id | As returned in `GET /servers` | "E8879F3D-95AE-4714-BC04-9B271C2BC52D" |
83+
| Name | Description | Sample |
84+
| ---- | --------------------------------- | -------------------------------------- |
85+
| Id | As returned in `GET /server/list` | "71552DCD-B298-4591-B59A-82DA4B07AEF8" |
7286

7387
###### Sample Return
7488

7589
```
7690
{
7791
"status": 1
78-
// 1 for succeed, 0 for fail
7992
}
8093
```
8194

82-
If the `id` is invalid or fail to match any `id` in config, "status" = 0.
95+
If the `Id` is invalid or fail to match any id in config, `"status": 0`.
96+
97+
- #### Add Server / Modify Existing Server `POST /server `
98+
99+
###### Argument
100+
101+
| Name | Sample |
102+
| ------------- | ---------------------- |
103+
| ServerPort | 49234 |
104+
| ServerHost | jp1-sta40.somehost.com |
105+
| Remark | jp1 |
106+
| PluginOptions | |
107+
| Plugin | |
108+
| Password | Password |
109+
| Method | chacha20-ietf-poly1305 |
110+
111+
To indicate modification, pass `Id` in addition.
112+
113+
| Name | Description | Sample |
114+
| ---- | --------------------------------- | -------------------------------------- |
115+
| Id | As returned in `GET /server/list` | "71552DCD-B298-4591-B59A-82DA4B07AEF8" |
116+
117+
For meaning of the arguments, refer to `GET /server/list` and the Server Perferences Panel of the app.
118+
119+
###### Sample Return
120+
121+
```
122+
{
123+
"status": 1
124+
}
125+
```
126+
127+
- #### Delete Server `DELETE /server`
128+
129+
###### Argument
130+
131+
| Name | Description | Sample |
132+
| ---- | --------------------------------- | -------------------------------------- |
133+
| Id | As returned in `GET /server/list` | "71552DCD-B298-4591-B59A-82DA4B07AEF8" |
134+
135+
###### Sample Return
136+
137+
```
138+
{
139+
"status": 1
140+
}
141+
```
142+
143+
If `Id` == id of current server, operation will no effect, `"status":0`.
144+
145+
If `Id` not match, `"status":0`.
83146

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

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

96159
- #### Switch mode `POST /mode`
97160

161+
###### Argument
162+
163+
| Name | Description | Sample |
164+
| ---- | -------------------------- | -------- |
165+
| mode | As returned in `GET /mode` | "global" |
166+
98167
###### Sample Return
99168

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

107-
If the `mode`∉ {"auto", "global", "manual"}, "status" = 0.
176+
---
177+
178+
All json names are case sensitive. Be careful.
179+

ShadowsocksX-NG/HTTPUserProxy.swift

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,36 +35,92 @@ class HTTPUserProxy{
3535
return GCDWebServerDataResponse(jsonObject: ["enable":isOn], contentType: "json")
3636
})
3737

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

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

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

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

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

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

57-
let uuid = ((request as! GCDWebServerURLEncodedFormRequest).arguments["id"])as? String
56+
return GCDWebServerDataResponse(jsonObject: ["Id":self.SerMgr.activeProfileId], contentType: "json")
57+
})
58+
59+
apiserver.addHandler(forMethod: "POST", path: "/server/current", request: GCDWebServerURLEncodedFormRequest.self, processBlock: {request in
60+
61+
let uuid = ((request as! GCDWebServerURLEncodedFormRequest).arguments["Id"])as? String
5862
for each in self.SerMgr.profiles{
5963
if (each.uuid == uuid) {
6064
self.appdeleget.changeServer(uuid: uuid!)
6165
return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")
62-
66+
67+
}
68+
}
69+
return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
70+
})
71+
72+
apiserver.addHandler(forMethod: "POST", path: "/server", request: GCDWebServerURLEncodedFormRequest.self, processBlock: {request in
73+
74+
var data = ((request as! GCDWebServerURLEncodedFormRequest).arguments) as! [String: Any]
75+
data["ServerPort"] = Double(data["ServerPort"] as! String)
76+
let id = data["Id"] as? String
77+
if (id != nil) {
78+
for each in self.SerMgr.profiles{
79+
if (each.uuid == id) {
80+
ServerProfile.copy(fromDict: data, toProfile: each)
81+
if (each.isValid()) {
82+
self.SerMgr.save()
83+
self.appdeleget.updateServersMenu()
84+
return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")
85+
}
86+
}
6387
}
6488
}
89+
else {
90+
let profile = ServerProfile.fromDictionary(data)
91+
if (profile.isValid()) {
92+
self.SerMgr.profiles.append(profile)
93+
self.SerMgr.save()
94+
self.appdeleget.updateServersMenu()
95+
return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")
96+
}
97+
}
98+
6599
return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
66100
})
67101

102+
apiserver.addHandler(forMethod: "DELETE", path: "/server", request: GCDWebServerRequest.self
103+
, processBlock: {request in
104+
105+
let uuid = (request.query?["Id"])as! String
106+
107+
if (uuid == self.SerMgr.activeProfileId) {
108+
return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
109+
}
110+
111+
for i in 0..<self.SerMgr.profiles.count{
112+
if (self.SerMgr.profiles[i].uuid == uuid) {
113+
self.SerMgr.profiles.remove(at: i)
114+
115+
self.SerMgr.save()
116+
self.appdeleget.updateServersMenu()
117+
118+
return GCDWebServerDataResponse(jsonObject: ["status":1], contentType: "json")
119+
}
120+
}
121+
122+
return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
123+
})
68124

69125
apiserver.addHandler(forMethod: "GET", path: "/mode", request: GCDWebServerRequest.self, processBlock: {request in
70126
if let current = self.defaults.string(forKey: "ShadowsocksRunningMode"){
@@ -79,7 +135,7 @@ class HTTPUserProxy{
79135
if (arg != "auto" && arg != "global" && arg != "manual") {
80136
return GCDWebServerDataResponse(jsonObject: ["status":0], contentType: "json")
81137
}
82-
138+
83139
self.appdeleget.changeMode(mode: arg!)
84140

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

ShadowsocksX-NG/ServerProfile.swift

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,31 +122,34 @@ class ServerProfile: NSObject, NSCopying {
122122
return copy;
123123
}
124124

125-
static func fromDictionary(_ data:[String:Any?]) -> ServerProfile {
125+
static func copy(fromDict:[String:Any?], toProfile:ServerProfile) {
126126
let cp = {
127127
(profile: ServerProfile) in
128-
profile.serverHost = data["ServerHost"] as! String
129-
profile.serverPort = (data["ServerPort"] as! NSNumber).uint16Value
130-
profile.method = data["Method"] as! String
131-
profile.password = data["Password"] as! String
132-
if let remark = data["Remark"] {
128+
profile.serverHost = fromDict["ServerHost"] as! String
129+
profile.serverPort = (fromDict["ServerPort"] as! NSNumber).uint16Value
130+
profile.method = fromDict["Method"] as! String
131+
profile.password = fromDict["Password"] as! String
132+
if let remark = fromDict["Remark"] {
133133
profile.remark = remark as! String
134134
}
135-
if let plugin = data["Plugin"] as? String {
135+
if let plugin = fromDict["Plugin"] as? String {
136136
profile.plugin = plugin
137137
}
138-
if let pluginOptions = data["PluginOptions"] as? String {
138+
if let pluginOptions = fromDict["PluginOptions"] as? String {
139139
profile.pluginOptions = pluginOptions
140140
}
141141
}
142-
142+
cp(toProfile)
143+
}
144+
145+
static func fromDictionary(_ data:[String:Any?]) -> ServerProfile {
143146
if let id = data["Id"] as? String {
144147
let profile = ServerProfile(uuid: id)
145-
cp(profile)
148+
copy(fromDict: data, toProfile: profile)
146149
return profile
147150
} else {
148151
let profile = ServerProfile()
149-
cp(profile)
152+
copy(fromDict: data, toProfile: profile)
150153
return profile
151154
}
152155
}

0 commit comments

Comments
 (0)