mirror of
https://github.com/fatedier/frp.git
synced 2025-05-06 22:38:26 +00:00
Compare commits
1144 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6cbb26283c | ||
|
75edea3370 | ||
|
e687aef37e | ||
|
a23455a737 | ||
|
e208043323 | ||
|
a78814a2e9 | ||
|
773169e0c4 | ||
|
9757a351c6 | ||
|
1e8db66743 | ||
|
e0dd947e6a | ||
|
8b86e1473c | ||
|
b8d3ace113 | ||
|
450b8393bc | ||
|
27db6217ec | ||
|
6542dcd4ed | ||
|
092e5d3f94 | ||
|
01fed8d1a9 | ||
|
f47d8ab97f | ||
|
bb912d6c37 | ||
|
c73096f2bf | ||
|
0358113948 | ||
|
8593eff752 | ||
|
dff56cb0ca | ||
|
4383756fd4 | ||
|
6ba849fc75 | ||
|
9d5638cae6 | ||
|
62352c7ba5 | ||
|
f7a06cbe61 | ||
|
3a08c2aeb0 | ||
|
b14192a8d3 | ||
|
2466e65f43 | ||
|
2855ac71e3 | ||
|
fe4ca1b54e | ||
|
edd7cf8967 | ||
|
03c8d7bf96 | ||
|
2dcdb24cc4 | ||
|
d47e138bc9 | ||
|
f1fb2d721a | ||
|
ae73ec2fed | ||
|
e8045194cd | ||
|
69cc422edf | ||
|
b4d5d8c756 | ||
|
c6f9d8d403 | ||
|
939c490768 | ||
|
f390e4a401 | ||
|
77990c31ef | ||
|
e680acf42d | ||
|
522e2c94c1 | ||
|
301515d2e8 | ||
|
f0442d0cd5 | ||
|
9ced717d69 | ||
|
92cb0b30c2 | ||
|
e81b36c5ba | ||
|
d0d396becb | ||
|
ee3892798d | ||
|
405969085f | ||
|
c1893ee1b4 | ||
|
eaae212d2d | ||
|
885278c045 | ||
|
2626d6ed92 | ||
|
f3a71bc08f | ||
|
dd7e2e8473 | ||
|
07946e9752 | ||
|
e52727e01c | ||
|
ba937e9fbf | ||
|
d2d03a8fd9 | ||
|
590ccda677 | ||
|
86f90f4d27 | ||
|
f16ef00975 | ||
|
b36f3834eb | ||
|
c08be0fd92 | ||
|
bc5fb91c05 | ||
|
002831ea82 | ||
|
acf33db4e4 | ||
|
3585f5c0c0 | ||
|
8383d528d9 | ||
|
fa977c839f | ||
|
86c2ad78c8 | ||
|
d5589213c5 | ||
|
e0c979e98e | ||
|
e6ec5a509b | ||
|
43ba7bd338 | ||
|
49443cb2c6 | ||
|
32f09c4b60 | ||
|
f63b4d5c29 | ||
|
b3946489dd | ||
|
7ae3719b82 | ||
|
80cfd0938e | ||
|
52f66b05e6 | ||
|
b2b580be22 | ||
|
3e0c78233a | ||
|
b6361fb143 | ||
|
adb04e81e7 | ||
|
518ca2ceb2 | ||
|
4957fd23ee | ||
|
2f958c2095 | ||
|
dc34a68542 | ||
|
3529158f31 | ||
|
9152c59570 | ||
|
2af2cf7dbd | ||
|
cf025d6320 | ||
|
e8ace492a5 | ||
|
1c8bc0bfa8 | ||
|
b31c67d7c0 | ||
|
8023d147b0 | ||
|
6a488cc081 | ||
|
7418ae098d | ||
|
7999791708 | ||
|
f7efbfeec5 | ||
|
1e8806d26b | ||
|
d01f4a3ec1 | ||
|
596262d5e0 | ||
|
cdfa8fa66f | ||
|
256b87321d | ||
|
5b7b81a117 | ||
|
2a9a7a0e4a | ||
|
5e77c8e2d3 | ||
|
3bf6605e1a | ||
|
3540910879 | ||
|
2d67e2e0c6 | ||
|
cc2076970f | ||
|
e7652f4ccc | ||
|
e66e77cb8f | ||
|
1bc9d1a28e | ||
|
7ad62818bd | ||
|
9ecafeab40 | ||
|
95cf418963 | ||
|
6d9e0c20f6 | ||
|
97d3cf1a3b | ||
|
38f297a395 | ||
|
7c799ee921 | ||
|
69ae2b0b69 | ||
|
d5b41f1e14 | ||
|
8b432e179d | ||
|
f5d5a00eef | ||
|
526e809bd5 | ||
|
e8deb65c4b | ||
|
184223cb2f | ||
|
5760c1cf92 | ||
|
5c4d820eb4 | ||
|
46266e4d30 | ||
|
a6478aeac8 | ||
|
806b55c292 | ||
|
496b1f1078 | ||
|
9cb0726ebc | ||
|
31190c703d | ||
|
1452facf77 | ||
|
6d4d8e616d | ||
|
a7ad967231 | ||
|
01a0d557ef | ||
|
b9c24e9b69 | ||
|
df12cc2b9d | ||
|
7cc67e852e | ||
|
307d1bfa3f | ||
|
5eb8f3db03 | ||
|
3ae1a4f45a | ||
|
21d8e674f0 | ||
|
5e70d5bee0 | ||
|
5c8ea51eb5 | ||
|
bae0b4d7c0 | ||
|
74255f711e | ||
|
7cd02f5bd8 | ||
|
c95311d1a0 | ||
|
885b029fcf | ||
|
f1454e91f5 | ||
|
e9e12cf888 | ||
|
6430afcfa5 | ||
|
3235addaaa | ||
|
46ff40543a | ||
|
efcc028a3d | ||
|
90861b6821 | ||
|
8f105adbca | ||
|
b1789afbab | ||
|
88c7e8bf7c | ||
|
fc4e787fe2 | ||
|
4c4d5f0d0d | ||
|
801e8c6742 | ||
|
b146989703 | ||
|
685d7618f3 | ||
|
15a245766e | ||
|
e1cef053be | ||
|
9ba6a06470 | ||
|
ea08de668e | ||
|
de85c9455a | ||
|
cceab7e1b1 | ||
|
9aef3b9944 | ||
|
341a5e3e3a | ||
|
c7a0cfc66d | ||
|
555db9d272 | ||
|
98068402c8 | ||
|
4915852b9c | ||
|
756dd1ad5e | ||
|
c71efde303 | ||
|
9f029e3248 | ||
|
8095075719 | ||
|
2225a1781f | ||
|
0214b974dd | ||
|
738c53ce47 | ||
|
db52f07d34 | ||
|
f6b8645f56 | ||
|
2c2c4ecdbc | ||
|
3faae194d0 | ||
|
a22d6c9504 | ||
|
9800b4cfcf | ||
|
8f394dba27 | ||
|
fccd518512 | ||
|
968ba4d3a1 | ||
|
862b1642ba | ||
|
54eb704650 | ||
|
8c6303c1e5 | ||
|
871511ba52 | ||
|
cb6d7ba7f9 | ||
|
31f40aa913 | ||
|
2f59e967a0 | ||
|
fe8374e99b | ||
|
24f0b3afa5 | ||
|
39941117b6 | ||
|
6a1f9ad893 | ||
|
18ab58eb25 | ||
|
fa0593ae2c | ||
|
89fff7d11d | ||
|
38d42dbe4b | ||
|
aa31d7ad0b | ||
|
113e3b0b0d | ||
|
100148d925 | ||
|
6b3daffaf0 | ||
|
5e17bc7bf1 | ||
|
b1b8d9a82b | ||
|
24c7d1d9e2 | ||
|
d205c26480 | ||
|
0eecab06c1 | ||
|
ad3548d332 | ||
|
679992db25 | ||
|
5cfbb976f4 | ||
|
b03f0ad1e6 | ||
|
804f2910fd | ||
|
e2d28d9929 | ||
|
7678938c08 | ||
|
b2e3946800 | ||
|
af0b7939a7 | ||
|
2f66dc3e99 | ||
|
649df8827c | ||
|
da51adc276 | ||
|
e5af37bc8c | ||
|
e8c8d5903a | ||
|
34ab6b0e74 | ||
|
cf66ca10b4 | ||
|
3fbe6b659e | ||
|
6a71d71e58 | ||
|
6ecc97c857 | ||
|
ba492f07c3 | ||
|
9d077b02cf | ||
|
f4e4fbea62 | ||
|
3e721d122b | ||
|
1bc899ec12 | ||
|
6f2571980c | ||
|
fa7c05c617 | ||
|
218b354f82 | ||
|
c652b8ef07 | ||
|
5b8b145577 | ||
|
0711295b0a | ||
|
4af85da0c2 | ||
|
bd89eaba2f | ||
|
a72259c604 | ||
|
44eb513f05 | ||
|
6c658586f6 | ||
|
888ed25314 | ||
|
21240ed962 | ||
|
6481870d03 | ||
|
a7a4ba270d | ||
|
915d9f4c09 | ||
|
18a2af4703 | ||
|
305e40fa8a | ||
|
4acae540c8 | ||
|
11b13533a0 | ||
|
100d556336 | ||
|
452fe25cc6 | ||
|
63efa6b776 | ||
|
37c27169ac | ||
|
1f88a7a0b8 | ||
|
eeea7602d9 | ||
|
bf635c0e90 | ||
|
cd31359a27 | ||
|
19739ed31a | ||
|
10100c28d9 | ||
|
ddc1e163c4 | ||
|
d20a6d3d75 | ||
|
6194273615 | ||
|
b2311e55e7 | ||
|
07873d471f | ||
|
9ca2b586f8 | ||
|
e59eacb8a2 | ||
|
0db4fc07fb | ||
|
70f4caac23 | ||
|
293003fcdb | ||
|
4bfc89d988 | ||
|
22412851b4 | ||
|
e9775bd70f | ||
|
ff7b8b0b62 | ||
|
491c1d7dc4 | ||
|
ea568e8a4f | ||
|
0fb6aeef58 | ||
|
032f33fe5a | ||
|
bbc8b438d5 | ||
|
05b1ace21f | ||
|
cbdd73b94f | ||
|
bf06e3b107 | ||
|
71489d194c | ||
|
85aa3df256 | ||
|
f1a51eba18 | ||
|
1d26ea440b | ||
|
998e678a7f | ||
|
0cee1877e3 | ||
|
72a7fd948e | ||
|
357c9b0dcb | ||
|
14bd0716d0 | ||
|
2f74f54f18 | ||
|
a62a9431b1 | ||
|
42745a3da2 | ||
|
82f80a22be | ||
|
f570dcb307 | ||
|
87e60683ed | ||
|
86b2e686a5 | ||
|
09f39de74e | ||
|
2a68c1152f | ||
|
df5859b5f7 | ||
|
3dd888a9ea | ||
|
a51e221db3 | ||
|
fe4e9b55f3 | ||
|
3f11b6a082 | ||
|
8a333c2ae0 | ||
|
1fd6ba2738 | ||
|
a98a9616f6 | ||
|
95cd9ab900 | ||
|
900454e58b | ||
|
c7d4637382 | ||
|
56925961df | ||
|
2393923870 | ||
|
5f594e9a71 | ||
|
8637077d90 | ||
|
ccb85a9926 | ||
|
02b12df887 | ||
|
c32a2ed140 | ||
|
9ae322cccf | ||
|
9cebfccb39 | ||
|
630dad50ed | ||
|
0d84da91d4 | ||
|
2408f1df04 | ||
|
fbaa5f866e | ||
|
9a849a29e9 | ||
|
6b80861bd6 | ||
|
fa0e84382e | ||
|
1a11b28f8d | ||
|
bed13d7ef1 | ||
|
e7d76b180d | ||
|
dba8925eaa | ||
|
fdef7448a7 | ||
|
0ff27fc9ac | ||
|
9f8db314d6 | ||
|
0c35863d97 | ||
|
184a0ff9ab | ||
|
8e25f13201 | ||
|
b5aee82ca9 | ||
|
0a2384a283 | ||
|
78b8bb7bc6 | ||
|
8fcd4f4a95 | ||
|
976fd81d4d | ||
|
52d5c9e25b | ||
|
fa89671452 | ||
|
3621aad1c1 | ||
|
a821db3f45 | ||
|
ecb6ed9258 | ||
|
b26080589b | ||
|
aff979c2b6 | ||
|
46f809d711 | ||
|
72595b2da8 | ||
|
c842558ace | ||
|
ed61049041 | ||
|
abe6f580c0 | ||
|
e940066012 | ||
|
1e846df870 | ||
|
0ab055e946 | ||
|
fca59c71e2 | ||
|
fae2f8768d | ||
|
3d9499f554 | ||
|
7adeeedd55 | ||
|
127a31ea6a | ||
|
a85bd9a4d9 | ||
|
01d551ec8d | ||
|
16cabf4127 | ||
|
968be4a2c2 | ||
|
8a779eb88c | ||
|
0138dbd352 | ||
|
9b45c93c14 | ||
|
191da54980 | ||
|
c3b7575453 | ||
|
7f7305fa03 | ||
|
644a0cfdb6 | ||
|
3c2e2bcea5 | ||
|
e52dfc4a5c | ||
|
cc003a2570 | ||
|
0f8040b875 | ||
|
ef5ae3e598 | ||
|
3acf1bb6e9 | ||
|
1089eb9d22 | ||
|
edf9596ca8 | ||
|
26e54b901f | ||
|
008933f304 | ||
|
317f901c1c | ||
|
cd5314466c | ||
|
3fbdea0f6b | ||
|
710ecf44f5 | ||
|
04dafd7ff0 | ||
|
c6aa74a2bb | ||
|
c0e05bb41e | ||
|
1e420cc766 | ||
|
4fff3c7472 | ||
|
48fa618c34 | ||
|
c9fe23eb10 | ||
|
268afb3438 | ||
|
b1181fd17a | ||
|
b23548eeff | ||
|
262317192c | ||
|
8b75b8b837 | ||
|
2170c481ce | ||
|
dfbf9c4542 | ||
|
964a1bbf39 | ||
|
228e225f84 | ||
|
bd6435c982 | ||
|
591023a1f0 | ||
|
1ab23b5e0e | ||
|
d193519329 | ||
|
7266154d54 | ||
|
4797136965 | ||
|
6d78af6144 | ||
|
7728e35c52 | ||
|
5a61fd84ad | ||
|
ad0c449a75 | ||
|
1c330185c4 | ||
|
7491b327f8 | ||
|
abb5b05d49 | ||
|
b6ec9dad28 | ||
|
caa6e8cf01 | ||
|
ffb932390f | ||
|
a8efaee1f3 | ||
|
4c2afb5c28 | ||
|
809f517db8 | ||
|
a4b105dedb | ||
|
10acf638f8 | ||
|
f65ffe2812 | ||
|
859a330e6c | ||
|
86ac511763 | ||
|
f2e98ef8a4 | ||
|
495d999b6c | ||
|
6d1af85e80 | ||
|
1db091b381 | ||
|
0b9124d4fd | ||
|
6c6607ae68 | ||
|
83d80857fd | ||
|
98fa3855bd | ||
|
9440bc5d72 | ||
|
95753ebf1c | ||
|
f8c6795119 | ||
|
7033f3e72b | ||
|
e3101b7aa8 | ||
|
c747f160aa | ||
|
c8748a2948 | ||
|
69fa7ed16e | ||
|
5336155365 | ||
|
4feb74cb89 | ||
|
4a4cf552af | ||
|
0f59b8f329 | ||
|
4832a2a1e9 | ||
|
52ecd84d8a | ||
|
42014eea23 | ||
|
c2da396230 | ||
|
e91c9473be | ||
|
13e48c6ca0 | ||
|
31e2cb76bb | ||
|
91e46a2c53 | ||
|
a57679f837 | ||
|
df18375308 | ||
|
c63737ab3e | ||
|
1cdceee347 | ||
|
694c434b9e | ||
|
62af5c8844 | ||
|
56c53909aa | ||
|
21a126e4e4 | ||
|
8affab1a2b | ||
|
12cc53d699 | ||
|
2ab832bb89 | ||
|
42425d8218 | ||
|
6da093a402 | ||
|
22a79710d8 | ||
|
0927553fe4 | ||
|
858d8f0ba7 | ||
|
8eb945ee9b | ||
|
dc0fd60d30 | ||
|
cd44c9f55c | ||
|
649f47c345 | ||
|
6ca3160b33 | ||
|
5f8ed4fc60 | ||
|
bf0993d2a6 | ||
|
5dc8175fc8 | ||
|
dc6a5a29c1 | ||
|
94212ac8b8 | ||
|
e9e86fccf0 | ||
|
58745992ef | ||
|
234d634bfe | ||
|
fdc6902a90 | ||
|
d8d587fd93 | ||
|
92791260a7 | ||
|
4dfd851c46 | ||
|
bc4df74b5e | ||
|
666f122a72 | ||
|
f999c8a87e | ||
|
90a32ab75d | ||
|
0713fd28da | ||
|
f5b33e6de8 | ||
|
fc6043bb4d | ||
|
bc46e3330a | ||
|
5fc7b3ceb5 | ||
|
6277af4790 | ||
|
00bd0a8af4 | ||
|
a415573e45 | ||
|
e68012858e | ||
|
ca8a5b753c | ||
|
d1f4ac0f2d | ||
|
ff357882ac | ||
|
934ac2b836 | ||
|
1ad50d5982 | ||
|
388b016842 | ||
|
50796643fb | ||
|
b1838b1d5e | ||
|
757b3613fe | ||
|
b657c0fe09 | ||
|
84df71047c | ||
|
abc6d720d0 | ||
|
80154639e3 | ||
|
f2117d8331 | ||
|
261be6a7b7 | ||
|
b53a2c1ed9 | ||
|
ee0df07a3c | ||
|
4e363eca2b | ||
|
4277405c0e | ||
|
6a99f0caf7 | ||
|
394af08561 | ||
|
30cb0a3ab0 | ||
|
5680a88267 | ||
|
6b089858db | ||
|
b3ed863021 | ||
|
5796c27ed5 | ||
|
310e8dd768 | ||
|
0b40ac2dbc | ||
|
f22c8e0882 | ||
|
a388bb2c95 | ||
|
e611c44dea | ||
|
8e36e2bb67 | ||
|
541ad8d899 | ||
|
17cc0735d1 | ||
|
fd336a5503 | ||
|
802d1c1861 | ||
|
65fe0a1179 | ||
|
2d24879fa3 | ||
|
75383a95b3 | ||
|
95444ea46b | ||
|
9f9c01b520 | ||
|
285d1eba0d | ||
|
0dfd3a421c | ||
|
6a1f15b25e | ||
|
9f47c324b7 | ||
|
f0df6084af | ||
|
879ca47590 | ||
|
6a7efc81c9 | ||
|
12c5c553c3 | ||
|
988e9b1de3 | ||
|
db6bbc5187 | ||
|
c67b4e7b94 | ||
|
b7a73d3469 | ||
|
7f9d88c10a | ||
|
79237d2b94 | ||
|
9c4ec56491 | ||
|
74a8752570 | ||
|
a8ab4c5003 | ||
|
9cee263c91 | ||
|
c6bf6f59e6 | ||
|
4b7aef2196 | ||
|
f6d0046b5a | ||
|
84363266d2 | ||
|
9ac8f2a047 | ||
|
b2b55533b8 | ||
|
a4cfab689a | ||
|
c7df39074c | ||
|
fdcdccb0c2 | ||
|
e945c1667a | ||
|
87a4de4370 | ||
|
e1e2913b77 | ||
|
9be24db410 | ||
|
6b61cb3742 | ||
|
90b7f2080f | ||
|
d1f1c72a55 | ||
|
1925847ef8 | ||
|
8b216b0ca9 | ||
|
dbfeea99f3 | ||
|
5e64bbfa7c | ||
|
e691a40260 | ||
|
d812488767 | ||
|
3c03690ab7 | ||
|
3df27b9c04 | ||
|
ba45d29b7c | ||
|
3cf83f57a8 | ||
|
03e4318d79 | ||
|
178d134f46 | ||
|
cbf9c731a0 | ||
|
de4bfcc43c | ||
|
9737978f28 | ||
|
5bc7fe2cea | ||
|
65d8fe37c5 | ||
|
1723d7b651 | ||
|
2481dfab64 | ||
|
95a881a7d3 | ||
|
fe403ab328 | ||
|
66555dbb00 | ||
|
7f9ea48405 | ||
|
96d7e2da6f | ||
|
d879b8208b | ||
|
3585e456d4 | ||
|
1de8c3fc87 | ||
|
bbab3fe9ca | ||
|
48990da22e | ||
|
5543fc2a9a | ||
|
c41de6fd28 | ||
|
8c8fd9790e | ||
|
5a7ef3be74 | ||
|
d9b5e0bde0 | ||
|
05ca72dbf0 | ||
|
ef6f8bbf6c | ||
|
70ac7d3d11 | ||
|
385c4d3dd5 | ||
|
5e1983f7ed | ||
|
516cdbddb0 | ||
|
3954ceb93b | ||
|
2061ef11c8 | ||
|
71cbe5decc | ||
|
a2ccb6c190 | ||
|
5bdf530b7e | ||
|
5177570da4 | ||
|
0bd8f9cd9b | ||
|
649a2f2457 | ||
|
54916793f9 | ||
|
0b06c1c821 | ||
|
bbc6f1687d | ||
|
b250342e27 | ||
|
f76deb8898 | ||
|
611d063e1f | ||
|
0c7d778896 | ||
|
7c21906884 | ||
|
a4106ec4b7 | ||
|
655c52f9ce | ||
|
25cfda5768 | ||
|
b61cb14c8f | ||
|
d5ce4d4916 | ||
|
4f0ee5980d | ||
|
146956ac6e | ||
|
35278ad17f | ||
|
aea9f9fbcc | ||
|
08c17c3247 | ||
|
6934a18f95 | ||
|
5165b0821f | ||
|
0aec869513 | ||
|
826b9db5f2 | ||
|
89d1a1fb2b | ||
|
450e0b7148 | ||
|
a1ac002694 | ||
|
951d33d47c | ||
|
b33ea9274c | ||
|
3b3f3dc2b5 | ||
|
ec0b59732c | ||
|
bae1ecdc69 | ||
|
5c2ab5a749 | ||
|
1a8ac148ca | ||
|
698219b621 | ||
|
229740524e | ||
|
cbeeac06a5 | ||
|
66a69f873f | ||
|
fb13774457 | ||
|
f14ed87b29 | ||
|
07623027bc | ||
|
941ac25648 | ||
|
f645082d72 | ||
|
7793f55545 | ||
|
ca88b07ecf | ||
|
6e305db4be | ||
|
9bb08396c7 | ||
|
64136a3b3e | ||
|
b8037475ed | ||
|
082447f517 | ||
|
cc6486addb | ||
|
57417c83ae | ||
|
d74b45be5d | ||
|
0d02f291e3 | ||
|
42ee536dae | ||
|
c33b5152e7 | ||
|
b6c219aa97 | ||
|
bbc36be052 | ||
|
f5778349d5 | ||
|
71603c6d0b | ||
|
e64fcce417 | ||
|
629f2856b1 | ||
|
aeb9f2b64d | ||
|
85dd41c17b | ||
|
102408d37f | ||
|
495b577819 | ||
|
f56b49ad3b | ||
|
cb1bf71bef | ||
|
b9f062bef2 | ||
|
490019fb51 | ||
|
2e497274ba | ||
|
cf4136fe99 | ||
|
b1e9cff622 | ||
|
db2d1fce76 | ||
|
8579de9d3f | ||
|
0c35273759 | ||
|
6eb8146334 | ||
|
da78e3f52e | ||
|
e1918f6396 | ||
|
ad1e32fd2d | ||
|
3726f99b04 | ||
|
c8a7405992 | ||
|
040d198e36 | ||
|
1a6cbbb2d2 | ||
|
ea79e03bd0 | ||
|
3e349455a0 | ||
|
c7a457a045 | ||
|
0b0d5c982e | ||
|
c4f873c07a | ||
|
01b1df2b91 | ||
|
f1bea49314 | ||
|
2ffae3489b | ||
|
96b94d9164 | ||
|
76b04f52d1 | ||
|
97db0d187a | ||
|
d9aadab4cb | ||
|
a0fe2fc2c2 | ||
|
1464836f05 | ||
|
b2a2037032 | ||
|
071cbf4b15 | ||
|
20fcb58437 | ||
|
a27e3dda88 | ||
|
1dd7317c06 | ||
|
58a54bd0fb | ||
|
caec4982cc | ||
|
dd8f788ca4 | ||
|
8a6d6c534a | ||
|
39089cf262 | ||
|
55800dc29f | ||
|
04560c1896 | ||
|
178efd67f1 | ||
|
6ef5fb6391 | ||
|
1ae43e4d41 | ||
|
5db605ca02 | ||
|
e087301425 | ||
|
f45283dbdb | ||
|
b0959b3caa | ||
|
c5c89a2519 | ||
|
bebd1db22a | ||
|
cd37d22f3b | ||
|
30af32728a | ||
|
60ecd1d58c | ||
|
a60be8f562 | ||
|
c008b14d0f | ||
|
853892f3cd | ||
|
e43f9f5850 | ||
|
d5f30ccd6b | ||
|
b87df569e7 | ||
|
976cf3e9f8 | ||
|
371c401f5b | ||
|
69919e8ef9 | ||
|
9abbe33790 | ||
|
4a5c00286e | ||
|
dfb892c8f6 | ||
|
461c4c18fd | ||
|
00b9ba95ae | ||
|
c47aad348d | ||
|
4cb4da3afc | ||
|
c1f57da00d | ||
|
fe187eb8ec | ||
|
0f6f674a64 | ||
|
814afbe1f6 | ||
|
3fde9176c9 | ||
|
af7cca1a93 | ||
|
7dd28a14aa | ||
|
1325c59a4c | ||
|
82dc1e924f | ||
|
3166bdf3f0 | ||
|
8af70c8822 | ||
|
87763e8251 | ||
|
e9241aeb94 | ||
|
2eaf134042 | ||
|
1739e012d6 | ||
|
9e8980429f | ||
|
1d0865ca49 | ||
|
5c9909aeef | ||
|
456ce09061 | ||
|
ffc13b704a | ||
|
5d239127bb | ||
|
9b990adf96 | ||
|
44e8108910 | ||
|
1c35e9a0c6 | ||
|
8e719ff0ff | ||
|
637ddbce1f | ||
|
ce8fde793c | ||
|
eede31c064 | ||
|
41c41789b6 | ||
|
68dfc89bce | ||
|
8690075c0c | ||
|
33d8816ced | ||
|
90cd25ac21 | ||
|
ff28668cf2 | ||
|
a6f2736b80 | ||
|
902f6f84a5 | ||
|
cf9193a429 | ||
|
3f64d73ea9 | ||
|
a77c7e8625 | ||
|
14733dd109 | ||
|
74b75e8c57 | ||
|
63e6e0dc92 | ||
|
4d4a738aa9 | ||
|
1ed130e704 | ||
|
2e773d550b | ||
|
e155ff056e | ||
|
37210d9983 | ||
|
338d5bae37 | ||
|
3e62198612 | ||
|
4f7dfcdb31 | ||
|
5b08201e5d | ||
|
b2c846664d | ||
|
3f6799c06a | ||
|
9a5f0c23c4 | ||
|
afde0c515c | ||
|
584e098e8e | ||
|
37395b3ef5 | ||
|
43fb3f3ff7 | ||
|
82b127494c | ||
|
4d79648657 | ||
|
3bb404dfb5 | ||
|
ff4bdec3f7 | ||
|
69f8b08ac0 | ||
|
d873df5ca8 | ||
|
a384bf5580 | ||
|
92046a7ca2 | ||
|
4cc5ddc012 | ||
|
46358d466d | ||
|
7da61f004b | ||
|
63037f1c65 | ||
|
cc160995da | ||
|
de48d97cb2 | ||
|
1a6a179b68 | ||
|
3a2946a2ff | ||
|
ae9a4623d9 | ||
|
bd1e9a3010 | ||
|
92fff5c191 | ||
|
8c65b337ca | ||
|
0f1005ff61 | ||
|
ad858a0d32 | ||
|
1e905839f0 | ||
|
bf50f932d9 | ||
|
673047be2c | ||
|
fa2b9a836c | ||
|
9e0fd0c4ef | ||
|
0559865fe5 | ||
|
4fc85a36c2 | ||
|
3f1174a519 | ||
|
bcbdfcb99b | ||
|
df046bdeeb | ||
|
f83447c652 | ||
|
9ae69b4aac | ||
|
c48a89731a | ||
|
36b58ab60c | ||
|
6320f15a7c | ||
|
066172e9c1 | ||
|
d5931758b6 | ||
|
c75c3acd21 | ||
|
0208ecd1d9 | ||
|
23e9845e65 | ||
|
2b1ba3a946 | ||
|
ee9ddf52cd | ||
|
d246400a71 | ||
|
f63a4f0cdd | ||
|
b743b5aaed | ||
|
9d9416ab94 | ||
|
c081df40e1 | ||
|
fe32a7c4bb | ||
|
7bb8c10647 | ||
|
0752508469 | ||
|
4cc1663a5f | ||
|
b55a24a27e | ||
|
aede4e54f8 | ||
|
b811a620c3 | ||
|
07fe05a9d5 | ||
|
171bc8dd22 | ||
|
9c175d4eb5 | ||
|
9f736558e2 | ||
|
8f071dd2c2 | ||
|
bcaf51a6ad | ||
|
ad3cf9a64a | ||
|
e3fc73dbc5 | ||
|
f884e894f2 | ||
|
d57ed7d3d8 | ||
|
a2c318d24c | ||
|
32f8745d61 | ||
|
66120fe49d | ||
|
fca7f42b37 | ||
|
5b303f5148 | ||
|
2a044c9d6d | ||
|
70e2aee46d | ||
|
6742fa2ea8 | ||
|
511503d34c | ||
|
1eaf17fd05 | ||
|
04f4fd0a81 | ||
|
3a4d769bb3 | ||
|
84341b7fcc | ||
|
80ba931326 | ||
|
7ebcc7503a | ||
|
74cf57feb3 | ||
|
712afed0ab | ||
|
e29a1330ed | ||
|
44971c7918 | ||
|
7bc6c72844 | ||
|
93461e0094 | ||
|
03d55201b2 | ||
|
e6d82f3162 | ||
|
1af6276be9 | ||
|
d1f5ec083a | ||
|
716ec281f6 | ||
|
67bfae5d23 | ||
|
f0dc3ed47b | ||
|
08b0885564 | ||
|
49b503c17b | ||
|
150682ec63 | ||
|
4dc96f41c9 | ||
|
6c13b6d37a | ||
|
1c04de380d | ||
|
738e5dad22 | ||
|
6d81e4c8c6 | ||
|
faf584e1dd | ||
|
ba6afd5789 | ||
|
11260389a1 | ||
|
b8082e6e08 | ||
|
7957572ced | ||
|
c2ff37d0d8 | ||
|
c67f9d5e76 | ||
|
1cc61b60f9 | ||
|
9c38baeb9e | ||
|
84465a7463 | ||
|
3fe50df200 | ||
|
93d86ca635 | ||
|
b600a07ec0 | ||
|
a5f06489cb | ||
|
2883d70ea9 | ||
|
3f17837a2c | ||
|
fd268b5082 | ||
|
69b09eb8a2 | ||
|
a84dd05351 | ||
|
71f7caa1ee | ||
|
5360febd72 | ||
|
1b70f0c4fd | ||
|
5c75efa222 | ||
|
ab4a53965b | ||
|
a0c83bdb78 | ||
|
30aeaf968e | ||
|
58d0d41501 | ||
|
6a95a63fd4 | ||
|
aa185eb9f3 | ||
|
d8683a0079 | ||
|
8b2cde3a30 | ||
|
634e048d0c | ||
|
a4fece3f51 | ||
|
9e683fe446 | ||
|
54bbfe26b0 | ||
|
a1023fdfc2 | ||
|
b02e1007fb | ||
|
f90028cf96 | ||
|
f83a2a73ab | ||
|
307b74cc13 | ||
|
f00a28598f | ||
|
6ee0b25782 | ||
|
88083d21e8 | ||
|
a22440aade | ||
|
b006540141 | ||
|
e655f07674 | ||
|
aafa96db58 | ||
|
1325148cd3 | ||
|
3f9749488a | ||
|
f9a0d891a1 | ||
|
92daa45b68 | ||
|
5f20a22b0d | ||
|
63be94c611 | ||
|
694ee44af6 | ||
|
edb97abf50 | ||
|
0c10279deb | ||
|
1f49510e3e | ||
|
1868b3bafb | ||
|
a23521885c | ||
|
c80dcd050d | ||
|
043ab62587 | ||
|
a8969b1901 | ||
|
e26285eefc | ||
|
299bd7b5cb | ||
|
90d1384bf7 | ||
|
a5434e31b7 | ||
|
044bb692dc | ||
|
34b98dde52 | ||
|
020f786bf5 | ||
|
cdcc1240ec | ||
|
c2c9f68a00 | ||
|
37470c26f0 | ||
|
04a4591caa | ||
|
8bf61d5e39 | ||
|
659f84bab2 | ||
|
9faf4acd62 | ||
|
4c3fb22295 | ||
|
d243f70125 | ||
|
a56f068f8c | ||
|
6a6ccc5302 | ||
|
6f90c3400c | ||
|
eb4f779384 | ||
|
59a34b81e0 | ||
|
b1d1a7a20a | ||
|
6b34ed4644 | ||
|
dde734c953 | ||
|
5532881b09 | ||
|
94ddeebc21 | ||
|
ddbb56ee8f | ||
|
10fc6c67e0 | ||
|
0573ddcd84 | ||
|
5eb5fec761 | ||
|
52fe721202 | ||
|
d7d2b72431 | ||
|
d04d31b39a | ||
|
d9304d8166 | ||
|
a44be1e2ed | ||
|
2bf1d3e922 | ||
|
19f349a65e | ||
|
b0e56945cd | ||
|
f2999e3317 | ||
|
a4c05e6ff9 | ||
|
d93dd82ed9 | ||
|
edf4bc431d | ||
|
47db75e921 | ||
|
c702355669 | ||
|
7cc5d03f35 | ||
|
54beb19435 | ||
|
396e148f80 | ||
|
4c69a4810e | ||
|
40e023f5f4 | ||
|
adcb2c1ea5 | ||
|
8c497793c5 | ||
|
78c6845781 | ||
|
dc5e130d33 | ||
|
fbc504dfa3 | ||
|
77f207d69a | ||
|
b65e037b5e | ||
|
b8a28e945c | ||
|
0476a85a7d | ||
|
5661537f7c | ||
|
19f7950485 | ||
|
c21f8ad291 | ||
|
3d6578b15f | ||
|
899d6837df | ||
|
0e1752b5ce | ||
|
da182ecd81 | ||
|
94c7f57949 | ||
|
c8e5096f48 | ||
|
5079bf01fd | ||
|
603d7df49a | ||
|
2b1c39e03d | ||
|
46ee2f2bc8 | ||
|
3d5c3acee0 | ||
|
41fd4bb673 | ||
|
e1e18ba9d6 | ||
|
ab9eff97a8 | ||
|
6f40b1a70a | ||
|
87c9b8f548 | ||
|
3fcf7efc5a | ||
|
a655f5699b | ||
|
09624b56ca | ||
|
e262ac6abd | ||
|
47c1a3e52c | ||
|
4dadaac905 | ||
|
e1ed6660b0 | ||
|
b71b2cf46d | ||
|
a0903d4121 | ||
|
b403e4142b | ||
|
46716acd8e | ||
|
c7f85bcdd3 | ||
|
ddd2acfe9f | ||
|
e3bf7e2b2b | ||
|
4914472215 | ||
|
5d9300c1e9 | ||
|
b4a577b0d7 | ||
|
32d0ce9ea0 | ||
|
2d30a6e8a7 | ||
|
740691b080 | ||
|
11fe4b1d8b | ||
|
c64931fce9 | ||
|
d4ecc2218d | ||
|
9c0ca8675d | ||
|
5cdb84c666 | ||
|
060277308b | ||
|
31dfd5101f | ||
|
4300169041 | ||
|
3ab9850871 | ||
|
d813b953dd | ||
|
1da81ad7d3 | ||
|
3b06d771ac | ||
|
7f386fc042 | ||
|
df8edefa56 | ||
|
ecb6ad4885 | ||
|
785dcaad44 | ||
|
fd3c97a0e9 | ||
|
8f5f0b0a9a | ||
|
452e02adab | ||
|
d2e1cfa5bc | ||
|
6dd51e0951 | ||
|
e0f2993b70 | ||
|
4067591a4d | ||
|
926d0b74a9 | ||
|
4f49458af0 | ||
|
fd6b94908b | ||
|
dee4cbd48c | ||
|
9a3564f29c | ||
|
ac09ba3982 | ||
|
a9bf25f255 | ||
|
6bc05de58e | ||
|
5265b79957 | ||
|
fefc0a38c3 | ||
|
c387138006 | ||
|
36f8beee3d | ||
|
366a0c898d | ||
|
d747f9207e | ||
|
5400366036 | ||
|
9dae7ad6fe | ||
|
a4e051d494 |
16
.circleci/config.yml
Normal file
16
.circleci/config.yml
Normal file
@ -0,0 +1,16 @@
|
||||
version: 2
|
||||
jobs:
|
||||
go-version-latest:
|
||||
docker:
|
||||
- image: cimg/go:1.23-node
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run: make
|
||||
- run: make alltest
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
jobs:
|
||||
- go-version-latest
|
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [fatedier]
|
||||
custom: ["https://afdian.com/a/fatedier"]
|
77
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
77
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
name: Bug report
|
||||
description: Report a bug to help us improve frp
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Bug Description
|
||||
description: Tell us what issues you ran into
|
||||
placeholder: Include information about what you tried, what you expected to happen, and what actually happened. The more details, the better!
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: frpc-version
|
||||
attributes:
|
||||
label: frpc Version
|
||||
description: Include the output of `frpc -v`
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: frps-version
|
||||
attributes:
|
||||
label: frps Version
|
||||
description: Include the output of `frps -v`
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: system-architecture
|
||||
attributes:
|
||||
label: System Architecture
|
||||
description: Include which architecture you used, such as `linux/amd64`, `windows/amd64`
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: config
|
||||
attributes:
|
||||
label: Configurations
|
||||
description: Include what configurrations you used and ran into this problem
|
||||
placeholder: Pay attention to hiding the token and password in your output
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Logs
|
||||
description: Prefer you providing releated error logs here
|
||||
placeholder: Pay attention to hiding your personal informations
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: How to reproduce it? It's important for us to find the bug
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
- type: checkboxes
|
||||
id: area
|
||||
attributes:
|
||||
label: Affected area
|
||||
options:
|
||||
- label: "Docs"
|
||||
- label: "Installation"
|
||||
- label: "Performance and Scalability"
|
||||
- label: "Security"
|
||||
- label: "User Experience"
|
||||
- label: "Test and Release"
|
||||
- label: "Developer Infrastructure"
|
||||
- label: "Client Plugin"
|
||||
- label: "Server Plugin"
|
||||
- label: "Extensions"
|
||||
- label: "Others"
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
36
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
36
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: Feature Request
|
||||
description: Suggest an idea to improve frp
|
||||
title: "[Feature Request] "
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
This is only used to request new product features.
|
||||
- type: textarea
|
||||
id: feature-request
|
||||
attributes:
|
||||
label: Describe the feature request
|
||||
description: Tell us what's you want and why it should be added in frp.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
- type: checkboxes
|
||||
id: area
|
||||
attributes:
|
||||
label: Affected area
|
||||
options:
|
||||
- label: "Docs"
|
||||
- label: "Installation"
|
||||
- label: "Performance and Scalability"
|
||||
- label: "Security"
|
||||
- label: "User Experience"
|
||||
- label: "Test and Release"
|
||||
- label: "Developer Infrastructure"
|
||||
- label: "Client Plugin"
|
||||
- label: "Server Plugin"
|
||||
- label: "Extensions"
|
||||
- label: "Others"
|
3
.github/pull_request_template.md
vendored
Normal file
3
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
### WHY
|
||||
|
||||
<!-- author to complete -->
|
83
.github/workflows/build-and-push-image.yml
vendored
Normal file
83
.github/workflows/build-and-push-image.yml
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
name: Build Image and Publish to Dockerhub & GPR
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [ published ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Image tag'
|
||||
required: true
|
||||
default: 'test'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
image:
|
||||
name: Build Image from Dockerfile and binaries
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# environment
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# get image tag name
|
||||
- name: Get Image Tag Name
|
||||
run: |
|
||||
if [ x${{ github.event.inputs.tag }} == x"" ]; then
|
||||
echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "TAG_NAME=${{ github.event.inputs.tag }}" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Login to the GPR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GPR_TOKEN }}
|
||||
|
||||
# prepare image tags
|
||||
- name: Prepare Image Tags
|
||||
run: |
|
||||
echo "DOCKERFILE_FRPC_PATH=dockerfiles/Dockerfile-for-frpc" >> $GITHUB_ENV
|
||||
echo "DOCKERFILE_FRPS_PATH=dockerfiles/Dockerfile-for-frps" >> $GITHUB_ENV
|
||||
echo "TAG_FRPC=fatedier/frpc:${{ env.TAG_NAME }}" >> $GITHUB_ENV
|
||||
echo "TAG_FRPS=fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV
|
||||
echo "TAG_FRPC_GPR=ghcr.io/fatedier/frpc:${{ env.TAG_NAME }}" >> $GITHUB_ENV
|
||||
echo "TAG_FRPS_GPR=ghcr.io/fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push frpc
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./dockerfiles/Dockerfile-for-frpc
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.TAG_FRPC }}
|
||||
${{ env.TAG_FRPC_GPR }}
|
||||
|
||||
- name: Build and push frps
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./dockerfiles/Dockerfile-for-frps
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.TAG_FRPS }}
|
||||
${{ env.TAG_FRPS_GPR }}
|
42
.github/workflows/golangci-lint.yml
vendored
Normal file
42
.github/workflows/golangci-lint.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
name: golangci-lint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
pull_request:
|
||||
permissions:
|
||||
contents: read
|
||||
# Optional: allow read access to pull request. Use with `only-new-issues` option.
|
||||
pull-requests: read
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
cache: false
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
with:
|
||||
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
||||
version: v1.61
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
# args: --issues-exit-code=0
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
# only-new-issues: true
|
||||
|
||||
# Optional: if set to true then the all caching functionality will be complete disabled,
|
||||
# takes precedence over all other caching options.
|
||||
# skip-cache: true
|
||||
|
||||
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
|
||||
# skip-pkg-cache: true
|
||||
|
||||
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
|
||||
# skip-build-cache: true
|
30
.github/workflows/goreleaser.yml
vendored
Normal file
30
.github/workflows/goreleaser.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: goreleaser
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
|
||||
- name: Make All
|
||||
run: |
|
||||
./package.sh
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean --release-notes=./Release.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GPR_TOKEN }}
|
35
.github/workflows/stale.yml
vendored
Normal file
35
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
name: "Close stale issues"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "20 0 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug-only:
|
||||
description: 'In debug mod'
|
||||
required: false
|
||||
default: 'false'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
permissions:
|
||||
issues: write # for actions/stale to close stale issues
|
||||
pull-requests: write # for actions/stale to close stale PRs
|
||||
actions: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
stale-issue-message: 'Issues go stale after 14d of inactivity. Stale issues rot after an additional 3d of inactivity and eventually close.'
|
||||
stale-pr-message: "PRs go stale after 14d of inactivity. Stale PRs rot after an additional 3d of inactivity and eventually close."
|
||||
stale-issue-label: 'lifecycle/stale'
|
||||
exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
|
||||
stale-pr-label: 'lifecycle/stale'
|
||||
exempt-pr-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
|
||||
days-before-stale: 14
|
||||
days-before-close: 3
|
||||
debug-only: ${{ github.event.inputs.debug-only }}
|
||||
exempt-all-pr-milestones: true
|
||||
exempt-all-pr-assignees: true
|
||||
operations-per-run: 200
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -26,7 +26,16 @@ _testmain.go
|
||||
# Self
|
||||
bin/
|
||||
packages/
|
||||
release/
|
||||
test/bin/
|
||||
vendor/
|
||||
lastversion/
|
||||
dist/
|
||||
.idea/
|
||||
.vscode/
|
||||
.autogen_ssh_key
|
||||
client.crt
|
||||
client.key
|
||||
|
||||
# Cache
|
||||
*.swp
|
||||
|
139
.golangci.yml
Normal file
139
.golangci.yml
Normal file
@ -0,0 +1,139 @@
|
||||
service:
|
||||
golangci-lint-version: 1.61.x # use the fixed version to not introduce new linters unexpectedly
|
||||
|
||||
run:
|
||||
concurrency: 4
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 20m
|
||||
build-tags:
|
||||
- integ
|
||||
- integfuzz
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- unused
|
||||
- errcheck
|
||||
- copyloopvar
|
||||
- gocritic
|
||||
- gofumpt
|
||||
- goimports
|
||||
- revive
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- lll
|
||||
- misspell
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- gci
|
||||
- gosec
|
||||
- asciicheck
|
||||
- prealloc
|
||||
- predeclared
|
||||
- makezero
|
||||
fast: false
|
||||
|
||||
linters-settings:
|
||||
errcheck:
|
||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: false
|
||||
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: false
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
disable:
|
||||
- shadow
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
misspell:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
locale: US
|
||||
ignore-words:
|
||||
- cancelled
|
||||
- marshalled
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 160
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- exitAfterDefer
|
||||
unused:
|
||||
check-exported: false
|
||||
unparam:
|
||||
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
||||
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
gci:
|
||||
sections:
|
||||
- standard
|
||||
- default
|
||||
- prefix(github.com/fatedier/frp/)
|
||||
gosec:
|
||||
severity: "low"
|
||||
confidence: "low"
|
||||
excludes:
|
||||
- G401
|
||||
- G402
|
||||
- G404
|
||||
- G501
|
||||
- G115 # integer overflow conversion
|
||||
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
# exclude:
|
||||
# - composite literal uses unkeyed fields
|
||||
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on test files.
|
||||
- path: _test\.go$|^tests/|^samples/
|
||||
linters:
|
||||
- errcheck
|
||||
- maligned
|
||||
- linters:
|
||||
- revive
|
||||
- stylecheck
|
||||
text: "use underscores in Go names"
|
||||
- linters:
|
||||
- revive
|
||||
text: "unused-parameter"
|
||||
- linters:
|
||||
- unparam
|
||||
text: "is always false"
|
||||
|
||||
exclude-dirs:
|
||||
- genfiles$
|
||||
- vendor$
|
||||
- bin$
|
||||
exclude-files:
|
||||
- ".*\\.pb\\.go"
|
||||
- ".*\\.gen\\.go"
|
||||
|
||||
# Independently from option `exclude` we use default exclude patterns,
|
||||
# it can be disabled by this option. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`.
|
||||
# Default value for this option is true.
|
||||
exclude-use-default: true
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-per-linter: 0
|
||||
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
max-same-issues: 0
|
22
.goreleaser.yml
Normal file
22
.goreleaser.yml
Normal file
@ -0,0 +1,22 @@
|
||||
builds:
|
||||
- skip: true
|
||||
checksum:
|
||||
name_template: '{{ .ProjectName }}_sha256_checksums.txt'
|
||||
algorithm: sha256
|
||||
extra_files:
|
||||
- glob: ./release/packages/*
|
||||
release:
|
||||
# Same as for github
|
||||
# Note: it can only be one: either github, gitlab or gitea
|
||||
github:
|
||||
owner: fatedier
|
||||
name: frp
|
||||
|
||||
draft: false
|
||||
|
||||
# You can add extra pre-existing files to the release.
|
||||
# The filename on the release will be the last part of the path (base). If
|
||||
# another file with the same name exists, the latest one found will be used.
|
||||
# Defaults to empty.
|
||||
extra_files:
|
||||
- glob: ./release/packages/*
|
12
.travis.yml
12
.travis.yml
@ -1,12 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.2
|
||||
- 1.5.3
|
||||
|
||||
install:
|
||||
- make
|
||||
|
||||
script:
|
||||
- make alltest
|
60
Godeps/Godeps.json
generated
60
Godeps/Godeps.json
generated
@ -1,60 +0,0 @@
|
||||
{
|
||||
"ImportPath": "frp",
|
||||
"GoVersion": "go1.4",
|
||||
"GodepVersion": "v62",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/astaxie/beego/logs",
|
||||
"Comment": "v1.5.0-9-gfb7314f",
|
||||
"Rev": "fb7314f8ac86b83ccd34386518d97cf2363e2ae5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docopt/docopt-go",
|
||||
"Comment": "0.6.2",
|
||||
"Rev": "784ddc588536785e7299f7272f39101f7faccc3f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/gin",
|
||||
"Comment": "v1.0rc1-262-g5caaac4",
|
||||
"Rev": "5caaac4c5c712a9e7a7de29e6c24ef46c753017f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/gin/binding",
|
||||
"Comment": "v1.0rc1-262-g5caaac4",
|
||||
"Rev": "5caaac4c5c712a9e7a7de29e6c24ef46c753017f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/gin/render",
|
||||
"Comment": "v1.0rc1-262-g5caaac4",
|
||||
"Rev": "5caaac4c5c712a9e7a7de29e6c24ef46c753017f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "7cc19b78d562895b13596ddce7aafb59dd789318"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/manucorporat/sse",
|
||||
"Rev": "ee05b128a739a0fb76c7ebd3ae4810c1de808d6d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vaughan0/go-ini",
|
||||
"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "3b993948b6f0e651ffb58ba135d8538a68b1cddf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/go-playground/validator.v8",
|
||||
"Comment": "v8.17.1",
|
||||
"Rev": "014792cf3e266caff1e916876be12282b33059e0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/yaml.v2",
|
||||
"Rev": "a83829b6f1293c91addabc89d0571c246397bbf4"
|
||||
}
|
||||
]
|
||||
}
|
5
Godeps/Readme
generated
5
Godeps/Readme
generated
@ -1,5 +0,0 @@
|
||||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
2
Godeps/_workspace/.gitignore
generated
vendored
2
Godeps/_workspace/.gitignore
generated
vendored
@ -1,2 +0,0 @@
|
||||
/pkg
|
||||
/bin
|
13
Godeps/_workspace/src/github.com/astaxie/beego/LICENSE
generated
vendored
13
Godeps/_workspace/src/github.com/astaxie/beego/LICENSE
generated
vendored
@ -1,13 +0,0 @@
|
||||
Copyright 2014 astaxie
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
63
Godeps/_workspace/src/github.com/astaxie/beego/logs/README.md
generated
vendored
63
Godeps/_workspace/src/github.com/astaxie/beego/logs/README.md
generated
vendored
@ -1,63 +0,0 @@
|
||||
## logs
|
||||
logs is a Go logs manager. It can use many logs adapters. The repo is inspired by `database/sql` .
|
||||
|
||||
|
||||
## How to install?
|
||||
|
||||
go get github.com/astaxie/beego/logs
|
||||
|
||||
|
||||
## What adapters are supported?
|
||||
|
||||
As of now this logs support console, file,smtp and conn.
|
||||
|
||||
|
||||
## How to use it?
|
||||
|
||||
First you must import it
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/logs"
|
||||
)
|
||||
|
||||
Then init a Log (example with console adapter)
|
||||
|
||||
log := NewLogger(10000)
|
||||
log.SetLogger("console", "")
|
||||
|
||||
> the first params stand for how many channel
|
||||
|
||||
Use it like this:
|
||||
|
||||
log.Trace("trace")
|
||||
log.Info("info")
|
||||
log.Warn("warning")
|
||||
log.Debug("debug")
|
||||
log.Critical("critical")
|
||||
|
||||
|
||||
## File adapter
|
||||
|
||||
Configure file adapter like this:
|
||||
|
||||
log := NewLogger(10000)
|
||||
log.SetLogger("file", `{"filename":"test.log"}`)
|
||||
|
||||
|
||||
## Conn adapter
|
||||
|
||||
Configure like this:
|
||||
|
||||
log := NewLogger(1000)
|
||||
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
|
||||
log.Info("info")
|
||||
|
||||
|
||||
## Smtp adapter
|
||||
|
||||
Configure like this:
|
||||
|
||||
log := NewLogger(10000)
|
||||
log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
|
||||
log.Critical("sendmail critical")
|
||||
time.Sleep(time.Second * 30)
|
116
Godeps/_workspace/src/github.com/astaxie/beego/logs/conn.go
generated
vendored
116
Godeps/_workspace/src/github.com/astaxie/beego/logs/conn.go
generated
vendored
@ -1,116 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ConnWriter implements LoggerInterface.
|
||||
// it writes messages in keep-live tcp connection.
|
||||
type ConnWriter struct {
|
||||
lg *log.Logger
|
||||
innerWriter io.WriteCloser
|
||||
ReconnectOnMsg bool `json:"reconnectOnMsg"`
|
||||
Reconnect bool `json:"reconnect"`
|
||||
Net string `json:"net"`
|
||||
Addr string `json:"addr"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// create new ConnWrite returning as LoggerInterface.
|
||||
func NewConn() LoggerInterface {
|
||||
conn := new(ConnWriter)
|
||||
conn.Level = LevelTrace
|
||||
return conn
|
||||
}
|
||||
|
||||
// init connection writer with json config.
|
||||
// json config only need key "level".
|
||||
func (c *ConnWriter) Init(jsonconfig string) error {
|
||||
return json.Unmarshal([]byte(jsonconfig), c)
|
||||
}
|
||||
|
||||
// write message in connection.
|
||||
// if connection is down, try to re-connect.
|
||||
func (c *ConnWriter) WriteMsg(msg string, level int) error {
|
||||
if level > c.Level {
|
||||
return nil
|
||||
}
|
||||
if c.neddedConnectOnMsg() {
|
||||
err := c.connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.ReconnectOnMsg {
|
||||
defer c.innerWriter.Close()
|
||||
}
|
||||
c.lg.Println(msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
// implementing method. empty.
|
||||
func (c *ConnWriter) Flush() {
|
||||
|
||||
}
|
||||
|
||||
// destroy connection writer and close tcp listener.
|
||||
func (c *ConnWriter) Destroy() {
|
||||
if c.innerWriter != nil {
|
||||
c.innerWriter.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConnWriter) connect() error {
|
||||
if c.innerWriter != nil {
|
||||
c.innerWriter.Close()
|
||||
c.innerWriter = nil
|
||||
}
|
||||
|
||||
conn, err := net.Dial(c.Net, c.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
c.innerWriter = conn
|
||||
c.lg = log.New(conn, "", log.Ldate|log.Ltime)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConnWriter) neddedConnectOnMsg() bool {
|
||||
if c.Reconnect {
|
||||
c.Reconnect = false
|
||||
return true
|
||||
}
|
||||
|
||||
if c.innerWriter == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return c.ReconnectOnMsg
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("conn", NewConn)
|
||||
}
|
95
Godeps/_workspace/src/github.com/astaxie/beego/logs/console.go
generated
vendored
95
Godeps/_workspace/src/github.com/astaxie/beego/logs/console.go
generated
vendored
@ -1,95 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type Brush func(string) string
|
||||
|
||||
func NewBrush(color string) Brush {
|
||||
pre := "\033["
|
||||
reset := "\033[0m"
|
||||
return func(text string) string {
|
||||
return pre + color + "m" + text + reset
|
||||
}
|
||||
}
|
||||
|
||||
var colors = []Brush{
|
||||
NewBrush("1;37"), // Emergency white
|
||||
NewBrush("1;36"), // Alert cyan
|
||||
NewBrush("1;35"), // Critical magenta
|
||||
NewBrush("1;31"), // Error red
|
||||
NewBrush("1;33"), // Warning yellow
|
||||
NewBrush("1;32"), // Notice green
|
||||
NewBrush("1;34"), // Informational blue
|
||||
NewBrush("1;34"), // Debug blue
|
||||
}
|
||||
|
||||
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
|
||||
type ConsoleWriter struct {
|
||||
lg *log.Logger
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// create ConsoleWriter returning as LoggerInterface.
|
||||
func NewConsole() LoggerInterface {
|
||||
cw := &ConsoleWriter{
|
||||
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
||||
Level: LevelDebug,
|
||||
}
|
||||
return cw
|
||||
}
|
||||
|
||||
// init console logger.
|
||||
// jsonconfig like '{"level":LevelTrace}'.
|
||||
func (c *ConsoleWriter) Init(jsonconfig string) error {
|
||||
if len(jsonconfig) == 0 {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal([]byte(jsonconfig), c)
|
||||
}
|
||||
|
||||
// write message in console.
|
||||
func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
|
||||
if level > c.Level {
|
||||
return nil
|
||||
}
|
||||
if goos := runtime.GOOS; goos == "windows" {
|
||||
c.lg.Println(msg)
|
||||
return nil
|
||||
}
|
||||
c.lg.Println(colors[level](msg))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// implementing method. empty.
|
||||
func (c *ConsoleWriter) Destroy() {
|
||||
|
||||
}
|
||||
|
||||
// implementing method. empty.
|
||||
func (c *ConsoleWriter) Flush() {
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("console", NewConsole)
|
||||
}
|
76
Godeps/_workspace/src/github.com/astaxie/beego/logs/es/es.go
generated
vendored
76
Godeps/_workspace/src/github.com/astaxie/beego/logs/es/es.go
generated
vendored
@ -1,76 +0,0 @@
|
||||
package es
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/belogik/goes"
|
||||
)
|
||||
|
||||
func NewES() logs.LoggerInterface {
|
||||
cw := &esLogger{
|
||||
Level: logs.LevelDebug,
|
||||
}
|
||||
return cw
|
||||
}
|
||||
|
||||
type esLogger struct {
|
||||
*goes.Connection
|
||||
DSN string `json:"dsn"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// {"dsn":"http://localhost:9200/","level":1}
|
||||
func (el *esLogger) Init(jsonconfig string) error {
|
||||
err := json.Unmarshal([]byte(jsonconfig), el)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if el.DSN == "" {
|
||||
return errors.New("empty dsn")
|
||||
} else if u, err := url.Parse(el.DSN); err != nil {
|
||||
return err
|
||||
} else if u.Path == "" {
|
||||
return errors.New("missing prefix")
|
||||
} else if host, port, err := net.SplitHostPort(u.Host); err != nil {
|
||||
return err
|
||||
} else {
|
||||
conn := goes.NewConnection(host, port)
|
||||
el.Connection = conn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (el *esLogger) WriteMsg(msg string, level int) error {
|
||||
if level > el.Level {
|
||||
return nil
|
||||
}
|
||||
t := time.Now()
|
||||
vals := make(map[string]interface{})
|
||||
vals["@timestamp"] = t.Format(time.RFC3339)
|
||||
vals["@msg"] = msg
|
||||
d := goes.Document{
|
||||
Index: fmt.Sprintf("%04d.%02d.%02d", t.Year(), t.Month(), t.Day()),
|
||||
Type: "logs",
|
||||
Fields: vals,
|
||||
}
|
||||
_, err := el.Index(d, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (el *esLogger) Destroy() {
|
||||
|
||||
}
|
||||
|
||||
func (el *esLogger) Flush() {
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
logs.Register("es", NewES)
|
||||
}
|
283
Godeps/_workspace/src/github.com/astaxie/beego/logs/file.go
generated
vendored
283
Godeps/_workspace/src/github.com/astaxie/beego/logs/file.go
generated
vendored
@ -1,283 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FileLogWriter implements LoggerInterface.
|
||||
// It writes messages by lines limit, file size limit, or time frequency.
|
||||
type FileLogWriter struct {
|
||||
*log.Logger
|
||||
mw *MuxWriter
|
||||
// The opened file
|
||||
Filename string `json:"filename"`
|
||||
|
||||
Maxlines int `json:"maxlines"`
|
||||
maxlines_curlines int
|
||||
|
||||
// Rotate at size
|
||||
Maxsize int `json:"maxsize"`
|
||||
maxsize_cursize int
|
||||
|
||||
// Rotate daily
|
||||
Daily bool `json:"daily"`
|
||||
Maxdays int64 `json:"maxdays"`
|
||||
daily_opendate int
|
||||
|
||||
Rotate bool `json:"rotate"`
|
||||
|
||||
startLock sync.Mutex // Only one log can write to the file
|
||||
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// an *os.File writer with locker.
|
||||
type MuxWriter struct {
|
||||
sync.Mutex
|
||||
fd *os.File
|
||||
}
|
||||
|
||||
// write to os.File.
|
||||
func (l *MuxWriter) Write(b []byte) (int, error) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
return l.fd.Write(b)
|
||||
}
|
||||
|
||||
// set os.File in writer.
|
||||
func (l *MuxWriter) SetFd(fd *os.File) {
|
||||
if l.fd != nil {
|
||||
l.fd.Close()
|
||||
}
|
||||
l.fd = fd
|
||||
}
|
||||
|
||||
// create a FileLogWriter returning as LoggerInterface.
|
||||
func NewFileWriter() LoggerInterface {
|
||||
w := &FileLogWriter{
|
||||
Filename: "",
|
||||
Maxlines: 1000000,
|
||||
Maxsize: 1 << 28, //256 MB
|
||||
Daily: true,
|
||||
Maxdays: 7,
|
||||
Rotate: true,
|
||||
Level: LevelTrace,
|
||||
}
|
||||
// use MuxWriter instead direct use os.File for lock write when rotate
|
||||
w.mw = new(MuxWriter)
|
||||
// set MuxWriter as Logger's io.Writer
|
||||
w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime)
|
||||
return w
|
||||
}
|
||||
|
||||
// Init file logger with json config.
|
||||
// jsonconfig like:
|
||||
// {
|
||||
// "filename":"logs/beego.log",
|
||||
// "maxlines":10000,
|
||||
// "maxsize":1<<30,
|
||||
// "daily":true,
|
||||
// "maxdays":15,
|
||||
// "rotate":true
|
||||
// }
|
||||
func (w *FileLogWriter) Init(jsonconfig string) error {
|
||||
err := json.Unmarshal([]byte(jsonconfig), w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(w.Filename) == 0 {
|
||||
return errors.New("jsonconfig must have filename")
|
||||
}
|
||||
err = w.startLogger()
|
||||
return err
|
||||
}
|
||||
|
||||
// start file logger. create log file and set to locker-inside file writer.
|
||||
func (w *FileLogWriter) startLogger() error {
|
||||
fd, err := w.createLogFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.mw.SetFd(fd)
|
||||
return w.initFd()
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) docheck(size int) {
|
||||
w.startLock.Lock()
|
||||
defer w.startLock.Unlock()
|
||||
if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) ||
|
||||
(w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) ||
|
||||
(w.Daily && time.Now().Day() != w.daily_opendate)) {
|
||||
if err := w.DoRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.maxlines_curlines++
|
||||
w.maxsize_cursize += size
|
||||
}
|
||||
|
||||
// write logger message into file.
|
||||
func (w *FileLogWriter) WriteMsg(msg string, level int) error {
|
||||
if level > w.Level {
|
||||
return nil
|
||||
}
|
||||
n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
|
||||
w.docheck(n)
|
||||
w.Logger.Println(msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) createLogFile() (*os.File, error) {
|
||||
// Open the log file
|
||||
fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
|
||||
return fd, err
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) initFd() error {
|
||||
fd := w.mw.fd
|
||||
finfo, err := fd.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get stat err: %s\n", err)
|
||||
}
|
||||
w.maxsize_cursize = int(finfo.Size())
|
||||
w.daily_opendate = time.Now().Day()
|
||||
w.maxlines_curlines = 0
|
||||
if finfo.Size() > 0 {
|
||||
count, err := w.lines()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.maxlines_curlines = count
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) lines() (int, error) {
|
||||
fd, err := os.Open(w.Filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
buf := make([]byte, 32768) // 32k
|
||||
count := 0
|
||||
lineSep := []byte{'\n'}
|
||||
|
||||
for {
|
||||
c, err := fd.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
return count, err
|
||||
}
|
||||
|
||||
count += bytes.Count(buf[:c], lineSep)
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// DoRotate means it need to write file in new file.
|
||||
// new file name like xx.log.2013-01-01.2
|
||||
func (w *FileLogWriter) DoRotate() error {
|
||||
_, err := os.Lstat(w.Filename)
|
||||
if err == nil { // file exists
|
||||
// Find the next available number
|
||||
num := 1
|
||||
fname := ""
|
||||
for ; err == nil && num <= 999; num++ {
|
||||
fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
|
||||
_, err = os.Lstat(fname)
|
||||
}
|
||||
// return error if the last file checked still existed
|
||||
if err == nil {
|
||||
return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.Filename)
|
||||
}
|
||||
|
||||
// block Logger's io.Writer
|
||||
w.mw.Lock()
|
||||
defer w.mw.Unlock()
|
||||
|
||||
fd := w.mw.fd
|
||||
fd.Close()
|
||||
|
||||
// close fd before rename
|
||||
// Rename the file to its newfound home
|
||||
err = os.Rename(w.Filename, fname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Rotate: %s\n", err)
|
||||
}
|
||||
|
||||
// re-start logger
|
||||
err = w.startLogger()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Rotate StartLogger: %s\n", err)
|
||||
}
|
||||
|
||||
go w.deleteOldLog()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) deleteOldLog() {
|
||||
dir := filepath.Dir(w.Filename)
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
|
||||
fmt.Println(returnErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
|
||||
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// destroy file logger, close file writer.
|
||||
func (w *FileLogWriter) Destroy() {
|
||||
w.mw.fd.Close()
|
||||
}
|
||||
|
||||
// flush file logger.
|
||||
// there are no buffering messages in file logger in memory.
|
||||
// flush file means sync file from disk.
|
||||
func (w *FileLogWriter) Flush() {
|
||||
w.mw.fd.Sync()
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("file", NewFileWriter)
|
||||
}
|
350
Godeps/_workspace/src/github.com/astaxie/beego/logs/log.go
generated
vendored
350
Godeps/_workspace/src/github.com/astaxie/beego/logs/log.go
generated
vendored
@ -1,350 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/astaxie/beego/logs"
|
||||
//
|
||||
// log := NewLogger(10000)
|
||||
// log.SetLogger("console", "")
|
||||
//
|
||||
// > the first params stand for how many channel
|
||||
//
|
||||
// Use it like this:
|
||||
//
|
||||
// log.Trace("trace")
|
||||
// log.Info("info")
|
||||
// log.Warn("warning")
|
||||
// log.Debug("debug")
|
||||
// log.Critical("critical")
|
||||
//
|
||||
// more docs http://beego.me/docs/module/logs.md
|
||||
package logs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// RFC5424 log message levels.
|
||||
const (
|
||||
LevelEmergency = iota
|
||||
LevelAlert
|
||||
LevelCritical
|
||||
LevelError
|
||||
LevelWarning
|
||||
LevelNotice
|
||||
LevelInformational
|
||||
LevelDebug
|
||||
)
|
||||
|
||||
// Legacy loglevel constants to ensure backwards compatibility.
|
||||
//
|
||||
// Deprecated: will be removed in 1.5.0.
|
||||
const (
|
||||
LevelInfo = LevelInformational
|
||||
LevelTrace = LevelDebug
|
||||
LevelWarn = LevelWarning
|
||||
)
|
||||
|
||||
type loggerType func() LoggerInterface
|
||||
|
||||
// LoggerInterface defines the behavior of a log provider.
|
||||
type LoggerInterface interface {
|
||||
Init(config string) error
|
||||
WriteMsg(msg string, level int) error
|
||||
Destroy()
|
||||
Flush()
|
||||
}
|
||||
|
||||
var adapters = make(map[string]loggerType)
|
||||
|
||||
// Register makes a log provide available by the provided name.
|
||||
// If Register is called twice with the same name or if driver is nil,
|
||||
// it panics.
|
||||
func Register(name string, log loggerType) {
|
||||
if log == nil {
|
||||
panic("logs: Register provide is nil")
|
||||
}
|
||||
if _, dup := adapters[name]; dup {
|
||||
panic("logs: Register called twice for provider " + name)
|
||||
}
|
||||
adapters[name] = log
|
||||
}
|
||||
|
||||
// BeeLogger is default logger in beego application.
|
||||
// it can contain several providers and log message into all providers.
|
||||
type BeeLogger struct {
|
||||
lock sync.Mutex
|
||||
level int
|
||||
enableFuncCallDepth bool
|
||||
loggerFuncCallDepth int
|
||||
asynchronous bool
|
||||
msg chan *logMsg
|
||||
outputs map[string]LoggerInterface
|
||||
}
|
||||
|
||||
type logMsg struct {
|
||||
level int
|
||||
msg string
|
||||
}
|
||||
|
||||
// NewLogger returns a new BeeLogger.
|
||||
// channellen means the number of messages in chan.
|
||||
// if the buffering chan is full, logger adapters write to file or other way.
|
||||
func NewLogger(channellen int64) *BeeLogger {
|
||||
bl := new(BeeLogger)
|
||||
bl.level = LevelDebug
|
||||
bl.loggerFuncCallDepth = 2
|
||||
bl.msg = make(chan *logMsg, channellen)
|
||||
bl.outputs = make(map[string]LoggerInterface)
|
||||
return bl
|
||||
}
|
||||
|
||||
func (bl *BeeLogger) Async() *BeeLogger {
|
||||
bl.asynchronous = true
|
||||
go bl.startLogger()
|
||||
return bl
|
||||
}
|
||||
|
||||
// SetLogger provides a given logger adapter into BeeLogger with config string.
|
||||
// config need to be correct JSON as string: {"interval":360}.
|
||||
func (bl *BeeLogger) SetLogger(adaptername string, config string) error {
|
||||
bl.lock.Lock()
|
||||
defer bl.lock.Unlock()
|
||||
if log, ok := adapters[adaptername]; ok {
|
||||
lg := log()
|
||||
err := lg.Init(config)
|
||||
bl.outputs[adaptername] = lg
|
||||
if err != nil {
|
||||
fmt.Println("logs.BeeLogger.SetLogger: " + err.Error())
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adaptername)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// remove a logger adapter in BeeLogger.
|
||||
func (bl *BeeLogger) DelLogger(adaptername string) error {
|
||||
bl.lock.Lock()
|
||||
defer bl.lock.Unlock()
|
||||
if lg, ok := bl.outputs[adaptername]; ok {
|
||||
lg.Destroy()
|
||||
delete(bl.outputs, adaptername)
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adaptername)
|
||||
}
|
||||
}
|
||||
|
||||
func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
|
||||
lm := new(logMsg)
|
||||
lm.level = loglevel
|
||||
if bl.enableFuncCallDepth {
|
||||
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
|
||||
if !ok {
|
||||
file = "???"
|
||||
line = 0
|
||||
}
|
||||
_, filename := path.Split(file)
|
||||
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
|
||||
} else {
|
||||
lm.msg = msg
|
||||
}
|
||||
if bl.asynchronous {
|
||||
bl.msg <- lm
|
||||
} else {
|
||||
for name, l := range bl.outputs {
|
||||
err := l.WriteMsg(lm.msg, lm.level)
|
||||
if err != nil {
|
||||
fmt.Println("unable to WriteMsg to adapter:", name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set log message level.
|
||||
//
|
||||
// If message level (such as LevelDebug) is higher than logger level (such as LevelWarning),
|
||||
// log providers will not even be sent the message.
|
||||
func (bl *BeeLogger) SetLevel(l int) {
|
||||
bl.level = l
|
||||
}
|
||||
|
||||
// set log funcCallDepth
|
||||
func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
|
||||
bl.loggerFuncCallDepth = d
|
||||
}
|
||||
|
||||
// get log funcCallDepth for wrapper
|
||||
func (bl *BeeLogger) GetLogFuncCallDepth() int {
|
||||
return bl.loggerFuncCallDepth
|
||||
}
|
||||
|
||||
// enable log funcCallDepth
|
||||
func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
|
||||
bl.enableFuncCallDepth = b
|
||||
}
|
||||
|
||||
// start logger chan reading.
|
||||
// when chan is not empty, write logs.
|
||||
func (bl *BeeLogger) startLogger() {
|
||||
for {
|
||||
select {
|
||||
case bm := <-bl.msg:
|
||||
for _, l := range bl.outputs {
|
||||
err := l.WriteMsg(bm.msg, bm.level)
|
||||
if err != nil {
|
||||
fmt.Println("ERROR, unable to WriteMsg:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log EMERGENCY level message.
|
||||
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
|
||||
if LevelEmergency > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[M] "+format, v...)
|
||||
bl.writerMsg(LevelEmergency, msg)
|
||||
}
|
||||
|
||||
// Log ALERT level message.
|
||||
func (bl *BeeLogger) Alert(format string, v ...interface{}) {
|
||||
if LevelAlert > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[A] "+format, v...)
|
||||
bl.writerMsg(LevelAlert, msg)
|
||||
}
|
||||
|
||||
// Log CRITICAL level message.
|
||||
func (bl *BeeLogger) Critical(format string, v ...interface{}) {
|
||||
if LevelCritical > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[C] "+format, v...)
|
||||
bl.writerMsg(LevelCritical, msg)
|
||||
}
|
||||
|
||||
// Log ERROR level message.
|
||||
func (bl *BeeLogger) Error(format string, v ...interface{}) {
|
||||
if LevelError > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[E] "+format, v...)
|
||||
bl.writerMsg(LevelError, msg)
|
||||
}
|
||||
|
||||
// Log WARNING level message.
|
||||
func (bl *BeeLogger) Warning(format string, v ...interface{}) {
|
||||
if LevelWarning > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[W] "+format, v...)
|
||||
bl.writerMsg(LevelWarning, msg)
|
||||
}
|
||||
|
||||
// Log NOTICE level message.
|
||||
func (bl *BeeLogger) Notice(format string, v ...interface{}) {
|
||||
if LevelNotice > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[N] "+format, v...)
|
||||
bl.writerMsg(LevelNotice, msg)
|
||||
}
|
||||
|
||||
// Log INFORMATIONAL level message.
|
||||
func (bl *BeeLogger) Informational(format string, v ...interface{}) {
|
||||
if LevelInformational > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[I] "+format, v...)
|
||||
bl.writerMsg(LevelInformational, msg)
|
||||
}
|
||||
|
||||
// Log DEBUG level message.
|
||||
func (bl *BeeLogger) Debug(format string, v ...interface{}) {
|
||||
if LevelDebug > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[D] "+format, v...)
|
||||
bl.writerMsg(LevelDebug, msg)
|
||||
}
|
||||
|
||||
// Log WARN level message.
|
||||
// compatibility alias for Warning()
|
||||
func (bl *BeeLogger) Warn(format string, v ...interface{}) {
|
||||
if LevelWarning > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[W] "+format, v...)
|
||||
bl.writerMsg(LevelWarning, msg)
|
||||
}
|
||||
|
||||
// Log INFO level message.
|
||||
// compatibility alias for Informational()
|
||||
func (bl *BeeLogger) Info(format string, v ...interface{}) {
|
||||
if LevelInformational > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[I] "+format, v...)
|
||||
bl.writerMsg(LevelInformational, msg)
|
||||
}
|
||||
|
||||
// Log TRACE level message.
|
||||
// compatibility alias for Debug()
|
||||
func (bl *BeeLogger) Trace(format string, v ...interface{}) {
|
||||
if LevelDebug > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[D] "+format, v...)
|
||||
bl.writerMsg(LevelDebug, msg)
|
||||
}
|
||||
|
||||
// flush all chan data.
|
||||
func (bl *BeeLogger) Flush() {
|
||||
for _, l := range bl.outputs {
|
||||
l.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// close logger, flush all chan data and destroy all adapters in BeeLogger.
|
||||
func (bl *BeeLogger) Close() {
|
||||
for {
|
||||
if len(bl.msg) > 0 {
|
||||
bm := <-bl.msg
|
||||
for _, l := range bl.outputs {
|
||||
err := l.WriteMsg(bm.msg, bm.level)
|
||||
if err != nil {
|
||||
fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
for _, l := range bl.outputs {
|
||||
l.Flush()
|
||||
l.Destroy()
|
||||
}
|
||||
}
|
165
Godeps/_workspace/src/github.com/astaxie/beego/logs/smtp.go
generated
vendored
165
Godeps/_workspace/src/github.com/astaxie/beego/logs/smtp.go
generated
vendored
@ -1,165 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// no usage
|
||||
// subjectPhrase = "Diagnostic message from server"
|
||||
)
|
||||
|
||||
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
|
||||
type SmtpWriter struct {
|
||||
Username string `json:"Username"`
|
||||
Password string `json:"password"`
|
||||
Host string `json:"Host"`
|
||||
Subject string `json:"subject"`
|
||||
FromAddress string `json:"fromAddress"`
|
||||
RecipientAddresses []string `json:"sendTos"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// create smtp writer.
|
||||
func NewSmtpWriter() LoggerInterface {
|
||||
return &SmtpWriter{Level: LevelTrace}
|
||||
}
|
||||
|
||||
// init smtp writer with json config.
|
||||
// config like:
|
||||
// {
|
||||
// "Username":"example@gmail.com",
|
||||
// "password:"password",
|
||||
// "host":"smtp.gmail.com:465",
|
||||
// "subject":"email title",
|
||||
// "fromAddress":"from@example.com",
|
||||
// "sendTos":["email1","email2"],
|
||||
// "level":LevelError
|
||||
// }
|
||||
func (s *SmtpWriter) Init(jsonconfig string) error {
|
||||
err := json.Unmarshal([]byte(jsonconfig), s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SmtpWriter) GetSmtpAuth(host string) smtp.Auth {
|
||||
if len(strings.Trim(s.Username, " ")) == 0 && len(strings.Trim(s.Password, " ")) == 0 {
|
||||
return nil
|
||||
}
|
||||
return smtp.PlainAuth(
|
||||
"",
|
||||
s.Username,
|
||||
s.Password,
|
||||
host,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SmtpWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAddress string, recipients []string, msgContent []byte) error {
|
||||
client, err := smtp.Dial(hostAddressWithPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, _, _ := net.SplitHostPort(hostAddressWithPort)
|
||||
tlsConn := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: host,
|
||||
}
|
||||
if err = client.StartTLS(tlsConn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if auth != nil {
|
||||
if err = client.Auth(auth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = client.Mail(fromAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rec := range recipients {
|
||||
if err = client.Rcpt(rec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
w, err := client.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write([]byte(msgContent))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.Quit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// write message in smtp writer.
|
||||
// it will send an email with subject and only this message.
|
||||
func (s *SmtpWriter) WriteMsg(msg string, level int) error {
|
||||
if level > s.Level {
|
||||
return nil
|
||||
}
|
||||
|
||||
hp := strings.Split(s.Host, ":")
|
||||
|
||||
// Set up authentication information.
|
||||
auth := s.GetSmtpAuth(hp[0])
|
||||
|
||||
// Connect to the server, authenticate, set the sender and recipient,
|
||||
// and send the email all in one step.
|
||||
content_type := "Content-Type: text/plain" + "; charset=UTF-8"
|
||||
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
|
||||
">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
|
||||
|
||||
return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
|
||||
}
|
||||
|
||||
// implementing method. empty.
|
||||
func (s *SmtpWriter) Flush() {
|
||||
return
|
||||
}
|
||||
|
||||
// implementing method. empty.
|
||||
func (s *SmtpWriter) Destroy() {
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("smtp", NewSmtpWriter)
|
||||
}
|
19
Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/LICENSE
generated
vendored
19
Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/LICENSE
generated
vendored
@ -1,19 +0,0 @@
|
||||
Copyright (c) 2011-2014 Dmitry Chestnykh <dmitry@codingrobots.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
25
Godeps/_workspace/src/github.com/docopt/docopt-go/.gitignore
generated
vendored
25
Godeps/_workspace/src/github.com/docopt/docopt-go/.gitignore
generated
vendored
@ -1,25 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
# coverage droppings
|
||||
profile.cov
|
31
Godeps/_workspace/src/github.com/docopt/docopt-go/.travis.yml
generated
vendored
31
Godeps/_workspace/src/github.com/docopt/docopt-go/.travis.yml
generated
vendored
@ -1,31 +0,0 @@
|
||||
# Travis CI (http://travis-ci.org/) is a continuous integration
|
||||
# service for open source projects. This file configures it
|
||||
# to run unit tests for docopt-go.
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/vet
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/golang/lint/golint
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
install:
|
||||
- go get -d -v ./... && go build -v ./...
|
||||
|
||||
script:
|
||||
- go vet -x ./...
|
||||
- $HOME/gopath/bin/golint ./...
|
||||
- go test -v ./...
|
||||
- go test -covermode=count -coverprofile=profile.cov .
|
||||
|
||||
after_script:
|
||||
- $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci
|
20
Godeps/_workspace/src/github.com/docopt/docopt-go/LICENSE
generated
vendored
20
Godeps/_workspace/src/github.com/docopt/docopt-go/LICENSE
generated
vendored
@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Keith Batten
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
88
Godeps/_workspace/src/github.com/docopt/docopt-go/README.md
generated
vendored
88
Godeps/_workspace/src/github.com/docopt/docopt-go/README.md
generated
vendored
@ -1,88 +0,0 @@
|
||||
docopt-go
|
||||
=========
|
||||
|
||||
[](https://travis-ci.org/docopt/docopt.go)
|
||||
[](https://coveralls.io/r/docopt/docopt.go)
|
||||
[](https://godoc.org/github.com/docopt/docopt.go)
|
||||
|
||||
An implementation of [docopt](http://docopt.org/) in the
|
||||
[Go](http://golang.org/) programming language.
|
||||
|
||||
**docopt** helps you create *beautiful* command-line interfaces easily:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/docopt/docopt-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
usage := `Naval Fate.
|
||||
|
||||
Usage:
|
||||
naval_fate ship new <name>...
|
||||
naval_fate ship <name> move <x> <y> [--speed=<kn>]
|
||||
naval_fate ship shoot <x> <y>
|
||||
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
|
||||
naval_fate -h | --help
|
||||
naval_fate --version
|
||||
|
||||
Options:
|
||||
-h --help Show this screen.
|
||||
--version Show version.
|
||||
--speed=<kn> Speed in knots [default: 10].
|
||||
--moored Moored (anchored) mine.
|
||||
--drifting Drifting mine.`
|
||||
|
||||
arguments, _ := docopt.Parse(usage, nil, true, "Naval Fate 2.0", false)
|
||||
fmt.Println(arguments)
|
||||
}
|
||||
```
|
||||
|
||||
**docopt** parses command-line arguments based on a help message. Don't
|
||||
write parser code: a good help message already has all the necessary
|
||||
information in it.
|
||||
|
||||
## Installation
|
||||
|
||||
⚠ Use the alias “docopt-go”. To use docopt in your Go code:
|
||||
|
||||
```go
|
||||
import "github.com/docopt/docopt-go"
|
||||
```
|
||||
|
||||
To install docopt according to your `$GOPATH`:
|
||||
|
||||
```console
|
||||
$ go get github.com/docopt/docopt-go
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
```go
|
||||
func Parse(doc string, argv []string, help bool, version string,
|
||||
optionsFirst bool, exit ...bool) (map[string]interface{}, error)
|
||||
```
|
||||
Parse `argv` based on the command-line interface described in `doc`.
|
||||
|
||||
Given a conventional command-line help message, docopt creates a parser and
|
||||
processes the arguments. See
|
||||
https://github.com/docopt/docopt#help-message-format for a description of the
|
||||
help message format. If `argv` is `nil`, `os.Args[1:]` is used.
|
||||
|
||||
docopt returns a map of option names to the values parsed from `argv`, and an
|
||||
error or `nil`.
|
||||
|
||||
More documentation for docopt is available at
|
||||
[GoDoc.org](https://godoc.org/github.com/docopt/docopt.go).
|
||||
|
||||
## Testing
|
||||
|
||||
All tests from the Python version are implemented and passing
|
||||
at [Travis CI](https://travis-ci.org/docopt/docopt.go). New
|
||||
language-agnostic tests have been added
|
||||
to [test_golang.docopt](test_golang.docopt).
|
||||
|
||||
To run tests for docopt-go, use `go test`.
|
1239
Godeps/_workspace/src/github.com/docopt/docopt-go/docopt.go
generated
vendored
1239
Godeps/_workspace/src/github.com/docopt/docopt-go/docopt.go
generated
vendored
File diff suppressed because it is too large
Load Diff
9
Godeps/_workspace/src/github.com/docopt/docopt-go/test_golang.docopt
generated
vendored
9
Godeps/_workspace/src/github.com/docopt/docopt-go/test_golang.docopt
generated
vendored
@ -1,9 +0,0 @@
|
||||
r"""usage: prog [NAME_-2]..."""
|
||||
$ prog 10 20
|
||||
{"NAME_-2": ["10", "20"]}
|
||||
|
||||
$ prog 10
|
||||
{"NAME_-2": ["10"]}
|
||||
|
||||
$ prog
|
||||
{"NAME_-2": []}
|
957
Godeps/_workspace/src/github.com/docopt/docopt-go/testcases.docopt
generated
vendored
957
Godeps/_workspace/src/github.com/docopt/docopt-go/testcases.docopt
generated
vendored
@ -1,957 +0,0 @@
|
||||
r"""Usage: prog
|
||||
|
||||
"""
|
||||
$ prog
|
||||
{}
|
||||
|
||||
$ prog --xxx
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
Options: -a All.
|
||||
|
||||
"""
|
||||
$ prog
|
||||
{"-a": false}
|
||||
|
||||
$ prog -a
|
||||
{"-a": true}
|
||||
|
||||
$ prog -x
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
Options: --all All.
|
||||
|
||||
"""
|
||||
$ prog
|
||||
{"--all": false}
|
||||
|
||||
$ prog --all
|
||||
{"--all": true}
|
||||
|
||||
$ prog --xxx
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
Options: -v, --verbose Verbose.
|
||||
|
||||
"""
|
||||
$ prog --verbose
|
||||
{"--verbose": true}
|
||||
|
||||
$ prog --ver
|
||||
{"--verbose": true}
|
||||
|
||||
$ prog -v
|
||||
{"--verbose": true}
|
||||
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
Options: -p PATH
|
||||
|
||||
"""
|
||||
$ prog -p home/
|
||||
{"-p": "home/"}
|
||||
|
||||
$ prog -phome/
|
||||
{"-p": "home/"}
|
||||
|
||||
$ prog -p
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
Options: --path <path>
|
||||
|
||||
"""
|
||||
$ prog --path home/
|
||||
{"--path": "home/"}
|
||||
|
||||
$ prog --path=home/
|
||||
{"--path": "home/"}
|
||||
|
||||
$ prog --pa home/
|
||||
{"--path": "home/"}
|
||||
|
||||
$ prog --pa=home/
|
||||
{"--path": "home/"}
|
||||
|
||||
$ prog --path
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
Options: -p PATH, --path=<path> Path to files.
|
||||
|
||||
"""
|
||||
$ prog -proot
|
||||
{"--path": "root"}
|
||||
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
Options: -p --path PATH Path to files.
|
||||
|
||||
"""
|
||||
$ prog -p root
|
||||
{"--path": "root"}
|
||||
|
||||
$ prog --path root
|
||||
{"--path": "root"}
|
||||
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
Options:
|
||||
-p PATH Path to files [default: ./]
|
||||
|
||||
"""
|
||||
$ prog
|
||||
{"-p": "./"}
|
||||
|
||||
$ prog -phome
|
||||
{"-p": "home"}
|
||||
|
||||
|
||||
r"""UsAgE: prog [options]
|
||||
|
||||
OpTiOnS: --path=<files> Path to files
|
||||
[dEfAuLt: /root]
|
||||
|
||||
"""
|
||||
$ prog
|
||||
{"--path": "/root"}
|
||||
|
||||
$ prog --path=home
|
||||
{"--path": "home"}
|
||||
|
||||
|
||||
r"""usage: prog [options]
|
||||
|
||||
options:
|
||||
-a Add
|
||||
-r Remote
|
||||
-m <msg> Message
|
||||
|
||||
"""
|
||||
$ prog -a -r -m Hello
|
||||
{"-a": true,
|
||||
"-r": true,
|
||||
"-m": "Hello"}
|
||||
|
||||
$ prog -armyourass
|
||||
{"-a": true,
|
||||
"-r": true,
|
||||
"-m": "yourass"}
|
||||
|
||||
$ prog -a -r
|
||||
{"-a": true,
|
||||
"-r": true,
|
||||
"-m": null}
|
||||
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
Options: --version
|
||||
--verbose
|
||||
|
||||
"""
|
||||
$ prog --version
|
||||
{"--version": true,
|
||||
"--verbose": false}
|
||||
|
||||
$ prog --verbose
|
||||
{"--version": false,
|
||||
"--verbose": true}
|
||||
|
||||
$ prog --ver
|
||||
"user-error"
|
||||
|
||||
$ prog --verb
|
||||
{"--version": false,
|
||||
"--verbose": true}
|
||||
|
||||
|
||||
r"""usage: prog [-a -r -m <msg>]
|
||||
|
||||
options:
|
||||
-a Add
|
||||
-r Remote
|
||||
-m <msg> Message
|
||||
|
||||
"""
|
||||
$ prog -armyourass
|
||||
{"-a": true,
|
||||
"-r": true,
|
||||
"-m": "yourass"}
|
||||
|
||||
|
||||
r"""usage: prog [-armmsg]
|
||||
|
||||
options: -a Add
|
||||
-r Remote
|
||||
-m <msg> Message
|
||||
|
||||
"""
|
||||
$ prog -a -r -m Hello
|
||||
{"-a": true,
|
||||
"-r": true,
|
||||
"-m": "Hello"}
|
||||
|
||||
|
||||
r"""usage: prog -a -b
|
||||
|
||||
options:
|
||||
-a
|
||||
-b
|
||||
|
||||
"""
|
||||
$ prog -a -b
|
||||
{"-a": true, "-b": true}
|
||||
|
||||
$ prog -b -a
|
||||
{"-a": true, "-b": true}
|
||||
|
||||
$ prog -a
|
||||
"user-error"
|
||||
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog (-a -b)
|
||||
|
||||
options: -a
|
||||
-b
|
||||
|
||||
"""
|
||||
$ prog -a -b
|
||||
{"-a": true, "-b": true}
|
||||
|
||||
$ prog -b -a
|
||||
{"-a": true, "-b": true}
|
||||
|
||||
$ prog -a
|
||||
"user-error"
|
||||
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog [-a] -b
|
||||
|
||||
options: -a
|
||||
-b
|
||||
|
||||
"""
|
||||
$ prog -a -b
|
||||
{"-a": true, "-b": true}
|
||||
|
||||
$ prog -b -a
|
||||
{"-a": true, "-b": true}
|
||||
|
||||
$ prog -a
|
||||
"user-error"
|
||||
|
||||
$ prog -b
|
||||
{"-a": false, "-b": true}
|
||||
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog [(-a -b)]
|
||||
|
||||
options: -a
|
||||
-b
|
||||
|
||||
"""
|
||||
$ prog -a -b
|
||||
{"-a": true, "-b": true}
|
||||
|
||||
$ prog -b -a
|
||||
{"-a": true, "-b": true}
|
||||
|
||||
$ prog -a
|
||||
"user-error"
|
||||
|
||||
$ prog -b
|
||||
"user-error"
|
||||
|
||||
$ prog
|
||||
{"-a": false, "-b": false}
|
||||
|
||||
|
||||
r"""usage: prog (-a|-b)
|
||||
|
||||
options: -a
|
||||
-b
|
||||
|
||||
"""
|
||||
$ prog -a -b
|
||||
"user-error"
|
||||
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
$ prog -a
|
||||
{"-a": true, "-b": false}
|
||||
|
||||
$ prog -b
|
||||
{"-a": false, "-b": true}
|
||||
|
||||
|
||||
r"""usage: prog [ -a | -b ]
|
||||
|
||||
options: -a
|
||||
-b
|
||||
|
||||
"""
|
||||
$ prog -a -b
|
||||
"user-error"
|
||||
|
||||
$ prog
|
||||
{"-a": false, "-b": false}
|
||||
|
||||
$ prog -a
|
||||
{"-a": true, "-b": false}
|
||||
|
||||
$ prog -b
|
||||
{"-a": false, "-b": true}
|
||||
|
||||
|
||||
r"""usage: prog <arg>"""
|
||||
$ prog 10
|
||||
{"<arg>": "10"}
|
||||
|
||||
$ prog 10 20
|
||||
"user-error"
|
||||
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog [<arg>]"""
|
||||
$ prog 10
|
||||
{"<arg>": "10"}
|
||||
|
||||
$ prog 10 20
|
||||
"user-error"
|
||||
|
||||
$ prog
|
||||
{"<arg>": null}
|
||||
|
||||
|
||||
r"""usage: prog <kind> <name> <type>"""
|
||||
$ prog 10 20 40
|
||||
{"<kind>": "10", "<name>": "20", "<type>": "40"}
|
||||
|
||||
$ prog 10 20
|
||||
"user-error"
|
||||
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog <kind> [<name> <type>]"""
|
||||
$ prog 10 20 40
|
||||
{"<kind>": "10", "<name>": "20", "<type>": "40"}
|
||||
|
||||
$ prog 10 20
|
||||
{"<kind>": "10", "<name>": "20", "<type>": null}
|
||||
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog [<kind> | <name> <type>]"""
|
||||
$ prog 10 20 40
|
||||
"user-error"
|
||||
|
||||
$ prog 20 40
|
||||
{"<kind>": null, "<name>": "20", "<type>": "40"}
|
||||
|
||||
$ prog
|
||||
{"<kind>": null, "<name>": null, "<type>": null}
|
||||
|
||||
|
||||
r"""usage: prog (<kind> --all | <name>)
|
||||
|
||||
options:
|
||||
--all
|
||||
|
||||
"""
|
||||
$ prog 10 --all
|
||||
{"<kind>": "10", "--all": true, "<name>": null}
|
||||
|
||||
$ prog 10
|
||||
{"<kind>": null, "--all": false, "<name>": "10"}
|
||||
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog [<name> <name>]"""
|
||||
$ prog 10 20
|
||||
{"<name>": ["10", "20"]}
|
||||
|
||||
$ prog 10
|
||||
{"<name>": ["10"]}
|
||||
|
||||
$ prog
|
||||
{"<name>": []}
|
||||
|
||||
|
||||
r"""usage: prog [(<name> <name>)]"""
|
||||
$ prog 10 20
|
||||
{"<name>": ["10", "20"]}
|
||||
|
||||
$ prog 10
|
||||
"user-error"
|
||||
|
||||
$ prog
|
||||
{"<name>": []}
|
||||
|
||||
|
||||
r"""usage: prog NAME..."""
|
||||
$ prog 10 20
|
||||
{"NAME": ["10", "20"]}
|
||||
|
||||
$ prog 10
|
||||
{"NAME": ["10"]}
|
||||
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog [NAME]..."""
|
||||
$ prog 10 20
|
||||
{"NAME": ["10", "20"]}
|
||||
|
||||
$ prog 10
|
||||
{"NAME": ["10"]}
|
||||
|
||||
$ prog
|
||||
{"NAME": []}
|
||||
|
||||
|
||||
r"""usage: prog [NAME...]"""
|
||||
$ prog 10 20
|
||||
{"NAME": ["10", "20"]}
|
||||
|
||||
$ prog 10
|
||||
{"NAME": ["10"]}
|
||||
|
||||
$ prog
|
||||
{"NAME": []}
|
||||
|
||||
|
||||
r"""usage: prog [NAME [NAME ...]]"""
|
||||
$ prog 10 20
|
||||
{"NAME": ["10", "20"]}
|
||||
|
||||
$ prog 10
|
||||
{"NAME": ["10"]}
|
||||
|
||||
$ prog
|
||||
{"NAME": []}
|
||||
|
||||
|
||||
r"""usage: prog (NAME | --foo NAME)
|
||||
|
||||
options: --foo
|
||||
|
||||
"""
|
||||
$ prog 10
|
||||
{"NAME": "10", "--foo": false}
|
||||
|
||||
$ prog --foo 10
|
||||
{"NAME": "10", "--foo": true}
|
||||
|
||||
$ prog --foo=10
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog (NAME | --foo) [--bar | NAME]
|
||||
|
||||
options: --foo
|
||||
options: --bar
|
||||
|
||||
"""
|
||||
$ prog 10
|
||||
{"NAME": ["10"], "--foo": false, "--bar": false}
|
||||
|
||||
$ prog 10 20
|
||||
{"NAME": ["10", "20"], "--foo": false, "--bar": false}
|
||||
|
||||
$ prog --foo --bar
|
||||
{"NAME": [], "--foo": true, "--bar": true}
|
||||
|
||||
|
||||
r"""Naval Fate.
|
||||
|
||||
Usage:
|
||||
prog ship new <name>...
|
||||
prog ship [<name>] move <x> <y> [--speed=<kn>]
|
||||
prog ship shoot <x> <y>
|
||||
prog mine (set|remove) <x> <y> [--moored|--drifting]
|
||||
prog -h | --help
|
||||
prog --version
|
||||
|
||||
Options:
|
||||
-h --help Show this screen.
|
||||
--version Show version.
|
||||
--speed=<kn> Speed in knots [default: 10].
|
||||
--moored Mored (anchored) mine.
|
||||
--drifting Drifting mine.
|
||||
|
||||
"""
|
||||
$ prog ship Guardian move 150 300 --speed=20
|
||||
{"--drifting": false,
|
||||
"--help": false,
|
||||
"--moored": false,
|
||||
"--speed": "20",
|
||||
"--version": false,
|
||||
"<name>": ["Guardian"],
|
||||
"<x>": "150",
|
||||
"<y>": "300",
|
||||
"mine": false,
|
||||
"move": true,
|
||||
"new": false,
|
||||
"remove": false,
|
||||
"set": false,
|
||||
"ship": true,
|
||||
"shoot": false}
|
||||
|
||||
|
||||
r"""usage: prog --hello"""
|
||||
$ prog --hello
|
||||
{"--hello": true}
|
||||
|
||||
|
||||
r"""usage: prog [--hello=<world>]"""
|
||||
$ prog
|
||||
{"--hello": null}
|
||||
|
||||
$ prog --hello wrld
|
||||
{"--hello": "wrld"}
|
||||
|
||||
|
||||
r"""usage: prog [-o]"""
|
||||
$ prog
|
||||
{"-o": false}
|
||||
|
||||
$ prog -o
|
||||
{"-o": true}
|
||||
|
||||
|
||||
r"""usage: prog [-opr]"""
|
||||
$ prog -op
|
||||
{"-o": true, "-p": true, "-r": false}
|
||||
|
||||
|
||||
r"""usage: prog --aabb | --aa"""
|
||||
$ prog --aa
|
||||
{"--aabb": false, "--aa": true}
|
||||
|
||||
$ prog --a
|
||||
"user-error" # not a unique prefix
|
||||
|
||||
#
|
||||
# Counting number of flags
|
||||
#
|
||||
|
||||
r"""Usage: prog -v"""
|
||||
$ prog -v
|
||||
{"-v": true}
|
||||
|
||||
|
||||
r"""Usage: prog [-v -v]"""
|
||||
$ prog
|
||||
{"-v": 0}
|
||||
|
||||
$ prog -v
|
||||
{"-v": 1}
|
||||
|
||||
$ prog -vv
|
||||
{"-v": 2}
|
||||
|
||||
|
||||
r"""Usage: prog -v ..."""
|
||||
$ prog
|
||||
"user-error"
|
||||
|
||||
$ prog -v
|
||||
{"-v": 1}
|
||||
|
||||
$ prog -vv
|
||||
{"-v": 2}
|
||||
|
||||
$ prog -vvvvvv
|
||||
{"-v": 6}
|
||||
|
||||
|
||||
r"""Usage: prog [-v | -vv | -vvv]
|
||||
|
||||
This one is probably most readable user-friednly variant.
|
||||
|
||||
"""
|
||||
$ prog
|
||||
{"-v": 0}
|
||||
|
||||
$ prog -v
|
||||
{"-v": 1}
|
||||
|
||||
$ prog -vv
|
||||
{"-v": 2}
|
||||
|
||||
$ prog -vvvv
|
||||
"user-error"
|
||||
|
||||
|
||||
r"""usage: prog [--ver --ver]"""
|
||||
$ prog --ver --ver
|
||||
{"--ver": 2}
|
||||
|
||||
|
||||
#
|
||||
# Counting commands
|
||||
#
|
||||
|
||||
r"""usage: prog [go]"""
|
||||
$ prog go
|
||||
{"go": true}
|
||||
|
||||
|
||||
r"""usage: prog [go go]"""
|
||||
$ prog
|
||||
{"go": 0}
|
||||
|
||||
$ prog go
|
||||
{"go": 1}
|
||||
|
||||
$ prog go go
|
||||
{"go": 2}
|
||||
|
||||
$ prog go go go
|
||||
"user-error"
|
||||
|
||||
r"""usage: prog go..."""
|
||||
$ prog go go go go go
|
||||
{"go": 5}
|
||||
|
||||
#
|
||||
# [options] does not include options from usage-pattern
|
||||
#
|
||||
r"""usage: prog [options] [-a]
|
||||
|
||||
options: -a
|
||||
-b
|
||||
"""
|
||||
$ prog -a
|
||||
{"-a": true, "-b": false}
|
||||
|
||||
$ prog -aa
|
||||
"user-error"
|
||||
|
||||
#
|
||||
# Test [options] shourtcut
|
||||
#
|
||||
|
||||
r"""Usage: prog [options] A
|
||||
Options:
|
||||
-q Be quiet
|
||||
-v Be verbose.
|
||||
|
||||
"""
|
||||
$ prog arg
|
||||
{"A": "arg", "-v": false, "-q": false}
|
||||
|
||||
$ prog -v arg
|
||||
{"A": "arg", "-v": true, "-q": false}
|
||||
|
||||
$ prog -q arg
|
||||
{"A": "arg", "-v": false, "-q": true}
|
||||
|
||||
#
|
||||
# Test single dash
|
||||
#
|
||||
|
||||
r"""usage: prog [-]"""
|
||||
|
||||
$ prog -
|
||||
{"-": true}
|
||||
|
||||
$ prog
|
||||
{"-": false}
|
||||
|
||||
#
|
||||
# If argument is repeated, its value should always be a list
|
||||
#
|
||||
|
||||
r"""usage: prog [NAME [NAME ...]]"""
|
||||
|
||||
$ prog a b
|
||||
{"NAME": ["a", "b"]}
|
||||
|
||||
$ prog
|
||||
{"NAME": []}
|
||||
|
||||
#
|
||||
# Option's argument defaults to null/None
|
||||
#
|
||||
|
||||
r"""usage: prog [options]
|
||||
options:
|
||||
-a Add
|
||||
-m <msg> Message
|
||||
|
||||
"""
|
||||
$ prog -a
|
||||
{"-m": null, "-a": true}
|
||||
|
||||
#
|
||||
# Test options without description
|
||||
#
|
||||
|
||||
r"""usage: prog --hello"""
|
||||
$ prog --hello
|
||||
{"--hello": true}
|
||||
|
||||
r"""usage: prog [--hello=<world>]"""
|
||||
$ prog
|
||||
{"--hello": null}
|
||||
|
||||
$ prog --hello wrld
|
||||
{"--hello": "wrld"}
|
||||
|
||||
r"""usage: prog [-o]"""
|
||||
$ prog
|
||||
{"-o": false}
|
||||
|
||||
$ prog -o
|
||||
{"-o": true}
|
||||
|
||||
r"""usage: prog [-opr]"""
|
||||
$ prog -op
|
||||
{"-o": true, "-p": true, "-r": false}
|
||||
|
||||
r"""usage: git [-v | --verbose]"""
|
||||
$ prog -v
|
||||
{"-v": true, "--verbose": false}
|
||||
|
||||
r"""usage: git remote [-v | --verbose]"""
|
||||
$ prog remote -v
|
||||
{"remote": true, "-v": true, "--verbose": false}
|
||||
|
||||
#
|
||||
# Test empty usage pattern
|
||||
#
|
||||
|
||||
r"""usage: prog"""
|
||||
$ prog
|
||||
{}
|
||||
|
||||
r"""usage: prog
|
||||
prog <a> <b>
|
||||
"""
|
||||
$ prog 1 2
|
||||
{"<a>": "1", "<b>": "2"}
|
||||
|
||||
$ prog
|
||||
{"<a>": null, "<b>": null}
|
||||
|
||||
r"""usage: prog <a> <b>
|
||||
prog
|
||||
"""
|
||||
$ prog
|
||||
{"<a>": null, "<b>": null}
|
||||
|
||||
#
|
||||
# Option's argument should not capture default value from usage pattern
|
||||
#
|
||||
|
||||
r"""usage: prog [--file=<f>]"""
|
||||
$ prog
|
||||
{"--file": null}
|
||||
|
||||
r"""usage: prog [--file=<f>]
|
||||
|
||||
options: --file <a>
|
||||
|
||||
"""
|
||||
$ prog
|
||||
{"--file": null}
|
||||
|
||||
r"""Usage: prog [-a <host:port>]
|
||||
|
||||
Options: -a, --address <host:port> TCP address [default: localhost:6283].
|
||||
|
||||
"""
|
||||
$ prog
|
||||
{"--address": "localhost:6283"}
|
||||
|
||||
#
|
||||
# If option with argument could be repeated,
|
||||
# its arguments should be accumulated into a list
|
||||
#
|
||||
|
||||
r"""usage: prog --long=<arg> ..."""
|
||||
|
||||
$ prog --long one
|
||||
{"--long": ["one"]}
|
||||
|
||||
$ prog --long one --long two
|
||||
{"--long": ["one", "two"]}
|
||||
|
||||
#
|
||||
# Test multiple elements repeated at once
|
||||
#
|
||||
|
||||
r"""usage: prog (go <direction> --speed=<km/h>)..."""
|
||||
$ prog go left --speed=5 go right --speed=9
|
||||
{"go": 2, "<direction>": ["left", "right"], "--speed": ["5", "9"]}
|
||||
|
||||
#
|
||||
# Required options should work with option shortcut
|
||||
#
|
||||
|
||||
r"""usage: prog [options] -a
|
||||
|
||||
options: -a
|
||||
|
||||
"""
|
||||
$ prog -a
|
||||
{"-a": true}
|
||||
|
||||
#
|
||||
# If option could be repeated its defaults should be split into a list
|
||||
#
|
||||
|
||||
r"""usage: prog [-o <o>]...
|
||||
|
||||
options: -o <o> [default: x]
|
||||
|
||||
"""
|
||||
$ prog -o this -o that
|
||||
{"-o": ["this", "that"]}
|
||||
|
||||
$ prog
|
||||
{"-o": ["x"]}
|
||||
|
||||
r"""usage: prog [-o <o>]...
|
||||
|
||||
options: -o <o> [default: x y]
|
||||
|
||||
"""
|
||||
$ prog -o this
|
||||
{"-o": ["this"]}
|
||||
|
||||
$ prog
|
||||
{"-o": ["x", "y"]}
|
||||
|
||||
#
|
||||
# Test stacked option's argument
|
||||
#
|
||||
|
||||
r"""usage: prog -pPATH
|
||||
|
||||
options: -p PATH
|
||||
|
||||
"""
|
||||
$ prog -pHOME
|
||||
{"-p": "HOME"}
|
||||
|
||||
#
|
||||
# Issue 56: Repeated mutually exclusive args give nested lists sometimes
|
||||
#
|
||||
|
||||
r"""Usage: foo (--xx=x|--yy=y)..."""
|
||||
$ prog --xx=1 --yy=2
|
||||
{"--xx": ["1"], "--yy": ["2"]}
|
||||
|
||||
#
|
||||
# POSIXly correct tokenization
|
||||
#
|
||||
|
||||
r"""usage: prog [<input file>]"""
|
||||
$ prog f.txt
|
||||
{"<input file>": "f.txt"}
|
||||
|
||||
r"""usage: prog [--input=<file name>]..."""
|
||||
$ prog --input a.txt --input=b.txt
|
||||
{"--input": ["a.txt", "b.txt"]}
|
||||
|
||||
#
|
||||
# Issue 85: `[options]` shourtcut with multiple subcommands
|
||||
#
|
||||
|
||||
r"""usage: prog good [options]
|
||||
prog fail [options]
|
||||
|
||||
options: --loglevel=N
|
||||
|
||||
"""
|
||||
$ prog fail --loglevel 5
|
||||
{"--loglevel": "5", "fail": true, "good": false}
|
||||
|
||||
#
|
||||
# Usage-section syntax
|
||||
#
|
||||
|
||||
r"""usage:prog --foo"""
|
||||
$ prog --foo
|
||||
{"--foo": true}
|
||||
|
||||
r"""PROGRAM USAGE: prog --foo"""
|
||||
$ prog --foo
|
||||
{"--foo": true}
|
||||
|
||||
r"""Usage: prog --foo
|
||||
prog --bar
|
||||
NOT PART OF SECTION"""
|
||||
$ prog --foo
|
||||
{"--foo": true, "--bar": false}
|
||||
|
||||
r"""Usage:
|
||||
prog --foo
|
||||
prog --bar
|
||||
|
||||
NOT PART OF SECTION"""
|
||||
$ prog --foo
|
||||
{"--foo": true, "--bar": false}
|
||||
|
||||
r"""Usage:
|
||||
prog --foo
|
||||
prog --bar
|
||||
NOT PART OF SECTION"""
|
||||
$ prog --foo
|
||||
{"--foo": true, "--bar": false}
|
||||
|
||||
#
|
||||
# Options-section syntax
|
||||
#
|
||||
|
||||
r"""Usage: prog [options]
|
||||
|
||||
global options: --foo
|
||||
local options: --baz
|
||||
--bar
|
||||
other options:
|
||||
--egg
|
||||
--spam
|
||||
-not-an-option-
|
||||
|
||||
"""
|
||||
$ prog --baz --egg
|
||||
{"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false}
|
4
Godeps/_workspace/src/github.com/gin-gonic/gin/.gitignore
generated
vendored
4
Godeps/_workspace/src/github.com/gin-gonic/gin/.gitignore
generated
vendored
@ -1,4 +0,0 @@
|
||||
Godeps/*
|
||||
!Godeps/Godeps.json
|
||||
coverage.out
|
||||
count.out
|
23
Godeps/_workspace/src/github.com/gin-gonic/gin/.travis.yml
generated
vendored
23
Godeps/_workspace/src/github.com/gin-gonic/gin/.travis.yml
generated
vendored
@ -1,23 +0,0 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- go test -v -covermode=count -coverprofile=coverage.out
|
||||
|
||||
after_success:
|
||||
- goveralls -coverprofile=coverage.out -service=travis-ci -repotoken yFj7FrCeddvBzUaaCyG33jCLfWXeb93eA
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/acc2c57482e94b44f557
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: false # default: false
|
229
Godeps/_workspace/src/github.com/gin-gonic/gin/AUTHORS.md
generated
vendored
229
Godeps/_workspace/src/github.com/gin-gonic/gin/AUTHORS.md
generated
vendored
@ -1,229 +0,0 @@
|
||||
List of all the awesome people working to make Gin the best Web Framework in Go.
|
||||
|
||||
|
||||
|
||||
##gin 0.x series authors
|
||||
|
||||
**Maintainer:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho)
|
||||
|
||||
People and companies, who have contributed, in alphabetical order.
|
||||
|
||||
**@858806258 (杰哥)**
|
||||
- Fix typo in example
|
||||
|
||||
|
||||
**@achedeuzot (Klemen Sever)**
|
||||
- Fix newline debug printing
|
||||
|
||||
|
||||
**@adammck (Adam Mckaig)**
|
||||
- Add MIT license
|
||||
|
||||
|
||||
**@AlexanderChen1989 (Alexander)**
|
||||
- Typos in README
|
||||
|
||||
|
||||
**@alexanderdidenko (Aleksandr Didenko)**
|
||||
- Add support multipart/form-data
|
||||
|
||||
|
||||
**@alexandernyquist (Alexander Nyquist)**
|
||||
- Using template.Must to fix multiple return issue
|
||||
- ★ Added support for OPTIONS verb
|
||||
- ★ Setting response headers before calling WriteHeader
|
||||
- Improved documentation for model binding
|
||||
- ★ Added Content.Redirect()
|
||||
- ★ Added tons of Unit tests
|
||||
|
||||
|
||||
**@austinheap (Austin Heap)**
|
||||
- Added travis CI integration
|
||||
|
||||
|
||||
**@andredublin (Andre Dublin)**
|
||||
- Fix typo in comment
|
||||
|
||||
|
||||
**@bredov (Ludwig Valda Vasquez)**
|
||||
- Fix html templating in debug mode
|
||||
|
||||
|
||||
**@bluele (Jun Kimura)**
|
||||
- Fixes code examples in README
|
||||
|
||||
|
||||
**@chad-russell**
|
||||
- ★ Support for serializing gin.H into XML
|
||||
|
||||
|
||||
**@dickeyxxx (Jeff Dickey)**
|
||||
- Typos in README
|
||||
- Add example about serving static files
|
||||
|
||||
|
||||
**@donileo (Adonis)**
|
||||
- Add NoMethod handler
|
||||
|
||||
|
||||
**@dutchcoders (DutchCoders)**
|
||||
- ★ Fix security bug that allows client to spoof ip
|
||||
- Fix typo. r.HTMLTemplates -> SetHTMLTemplate
|
||||
|
||||
|
||||
**@el3ctro- (Joshua Loper)**
|
||||
- Fix typo in example
|
||||
|
||||
|
||||
**@ethankan (Ethan Kan)**
|
||||
- Unsigned integers in binding
|
||||
|
||||
|
||||
**(Evgeny Persienko)**
|
||||
- Validate sub structures
|
||||
|
||||
|
||||
**@frankbille (Frank Bille)**
|
||||
- Add support for HTTP Realm Auth
|
||||
|
||||
|
||||
**@fmd (Fareed Dudhia)**
|
||||
- Fix typo. SetHTTPTemplate -> SetHTMLTemplate
|
||||
|
||||
|
||||
**@ironiridis (Christopher Harrington)**
|
||||
- Remove old reference
|
||||
|
||||
|
||||
**@jammie-stackhouse (Jamie Stackhouse)**
|
||||
- Add more shortcuts for router methods
|
||||
|
||||
|
||||
**@jasonrhansen**
|
||||
- Fix spelling and grammar errors in documentation
|
||||
|
||||
|
||||
**@JasonSoft (Jason Lee)**
|
||||
- Fix typo in comment
|
||||
|
||||
|
||||
**@joiggama (Ignacio Galindo)**
|
||||
- Add utf-8 charset header on renders
|
||||
|
||||
|
||||
**@julienschmidt (Julien Schmidt)**
|
||||
- gofmt the code examples
|
||||
|
||||
|
||||
**@kelcecil (Kel Cecil)**
|
||||
- Fix readme typo
|
||||
|
||||
|
||||
**@kyledinh (Kyle Dinh)**
|
||||
- Adds RunTLS()
|
||||
|
||||
|
||||
**@LinusU (Linus Unnebäck)**
|
||||
- Small fixes in README
|
||||
|
||||
|
||||
**@loongmxbt (Saint Asky)**
|
||||
- Fix typo in example
|
||||
|
||||
|
||||
**@lucas-clemente (Lucas Clemente)**
|
||||
- ★ work around path.Join removing trailing slashes from routes
|
||||
|
||||
|
||||
**@mattn (Yasuhiro Matsumoto)**
|
||||
- Improve color logger
|
||||
|
||||
|
||||
**@mdigger (Dmitry Sedykh)**
|
||||
- Fixes Form binding when content-type is x-www-form-urlencoded
|
||||
- No repeat call c.Writer.Status() in gin.Logger
|
||||
- Fixes Content-Type for json render
|
||||
|
||||
|
||||
**@mirzac (Mirza Ceric)**
|
||||
- Fix debug printing
|
||||
|
||||
|
||||
**@mopemope (Yutaka Matsubara)**
|
||||
- ★ Adds Godep support (Dependencies Manager)
|
||||
- Fix variadic parameter in the flexible render API
|
||||
- Fix Corrupted plain render
|
||||
- Add Pluggable View Renderer Example
|
||||
|
||||
|
||||
**@msemenistyi (Mykyta Semenistyi)**
|
||||
- update Readme.md. Add code to String method
|
||||
|
||||
|
||||
**@msoedov (Sasha Myasoedov)**
|
||||
- ★ Adds tons of unit tests.
|
||||
|
||||
|
||||
**@ngerakines (Nick Gerakines)**
|
||||
- ★ Improves API, c.GET() doesn't panic
|
||||
- Adds MustGet() method
|
||||
|
||||
|
||||
**@r8k (Rajiv Kilaparti)**
|
||||
- Fix Port usage in README.
|
||||
|
||||
|
||||
**@rayrod2030 (Ray Rodriguez)**
|
||||
- Fix typo in example
|
||||
|
||||
|
||||
**@rns**
|
||||
- Fix typo in example
|
||||
|
||||
|
||||
**@RobAWilkinson (Robert Wilkinson)**
|
||||
- Add example of forms and params
|
||||
|
||||
|
||||
**@rogierlommers (Rogier Lommers)**
|
||||
- Add updated static serve example
|
||||
|
||||
|
||||
**@se77en (Damon Zhao)**
|
||||
- Improve color logging
|
||||
|
||||
|
||||
**@silasb (Silas Baronda)**
|
||||
- Fixing quotes in README
|
||||
|
||||
|
||||
**@SkuliOskarsson (Skuli Oskarsson)**
|
||||
- Fixes some texts in README II
|
||||
|
||||
|
||||
**@slimmy (Jimmy Pettersson)**
|
||||
- Added messages for required bindings
|
||||
|
||||
|
||||
**@smira (Andrey Smirnov)**
|
||||
- Add support for ignored/unexported fields in binding
|
||||
|
||||
|
||||
**@superalsrk (SRK.Lyu)**
|
||||
- Update httprouter godeps
|
||||
|
||||
|
||||
**@tebeka (Miki Tebeka)**
|
||||
- Use net/http constants instead of numeric values
|
||||
|
||||
|
||||
**@techjanitor**
|
||||
- Update context.go reserved IPs
|
||||
|
||||
|
||||
**@yosssi (Keiji Yoshida)**
|
||||
- Fix link in README
|
||||
|
||||
|
||||
**@yuyabee**
|
||||
- Fixed README
|
298
Godeps/_workspace/src/github.com/gin-gonic/gin/BENCHMARKS.md
generated
vendored
298
Godeps/_workspace/src/github.com/gin-gonic/gin/BENCHMARKS.md
generated
vendored
@ -1,298 +0,0 @@
|
||||
**Machine:** intel i7 ivy bridge quad-core. 8GB RAM.
|
||||
**Date:** June 4th, 2015
|
||||
[https://github.com/gin-gonic/go-http-routing-benchmark](https://github.com/gin-gonic/go-http-routing-benchmark)
|
||||
|
||||
```
|
||||
BenchmarkAce_Param 5000000 372 ns/op 32 B/op 1 allocs/op
|
||||
BenchmarkBear_Param 1000000 1165 ns/op 424 B/op 5 allocs/op
|
||||
BenchmarkBeego_Param 1000000 2440 ns/op 720 B/op 10 allocs/op
|
||||
BenchmarkBone_Param 1000000 1067 ns/op 384 B/op 3 allocs/op
|
||||
BenchmarkDenco_Param 5000000 240 ns/op 32 B/op 1 allocs/op
|
||||
BenchmarkEcho_Param 10000000 130 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_Param 10000000 133 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_Param 1000000 1826 ns/op 656 B/op 9 allocs/op
|
||||
BenchmarkGoji_Param 2000000 957 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkGoJsonRest_Param 1000000 2021 ns/op 657 B/op 14 allocs/op
|
||||
BenchmarkGoRestful_Param 200000 8825 ns/op 2496 B/op 31 allocs/op
|
||||
BenchmarkGorillaMux_Param 500000 3340 ns/op 784 B/op 9 allocs/op
|
||||
BenchmarkHttpRouter_Param 10000000 152 ns/op 32 B/op 1 allocs/op
|
||||
BenchmarkHttpTreeMux_Param 2000000 717 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkKocha_Param 3000000 423 ns/op 56 B/op 3 allocs/op
|
||||
BenchmarkMacaron_Param 1000000 3410 ns/op 1104 B/op 11 allocs/op
|
||||
BenchmarkMartini_Param 200000 7101 ns/op 1152 B/op 12 allocs/op
|
||||
BenchmarkPat_Param 1000000 2040 ns/op 656 B/op 14 allocs/op
|
||||
BenchmarkPossum_Param 1000000 2048 ns/op 624 B/op 7 allocs/op
|
||||
BenchmarkR2router_Param 1000000 1144 ns/op 432 B/op 6 allocs/op
|
||||
BenchmarkRevel_Param 200000 6725 ns/op 1672 B/op 28 allocs/op
|
||||
BenchmarkRivet_Param 1000000 1121 ns/op 464 B/op 5 allocs/op
|
||||
BenchmarkTango_Param 1000000 1479 ns/op 256 B/op 10 allocs/op
|
||||
BenchmarkTigerTonic_Param 1000000 3393 ns/op 992 B/op 19 allocs/op
|
||||
BenchmarkTraffic_Param 300000 5525 ns/op 1984 B/op 23 allocs/op
|
||||
BenchmarkVulcan_Param 2000000 924 ns/op 98 B/op 3 allocs/op
|
||||
BenchmarkZeus_Param 1000000 1084 ns/op 368 B/op 3 allocs/op
|
||||
BenchmarkAce_Param5 3000000 614 ns/op 160 B/op 1 allocs/op
|
||||
BenchmarkBear_Param5 1000000 1617 ns/op 469 B/op 5 allocs/op
|
||||
BenchmarkBeego_Param5 1000000 3373 ns/op 992 B/op 13 allocs/op
|
||||
BenchmarkBone_Param5 1000000 1478 ns/op 432 B/op 3 allocs/op
|
||||
BenchmarkDenco_Param5 3000000 570 ns/op 160 B/op 1 allocs/op
|
||||
BenchmarkEcho_Param5 5000000 256 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_Param5 10000000 222 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_Param5 1000000 2789 ns/op 928 B/op 12 allocs/op
|
||||
BenchmarkGoji_Param5 1000000 1287 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkGoJsonRest_Param5 1000000 3670 ns/op 1105 B/op 17 allocs/op
|
||||
BenchmarkGoRestful_Param5 200000 10756 ns/op 2672 B/op 31 allocs/op
|
||||
BenchmarkGorillaMux_Param5 300000 5543 ns/op 912 B/op 9 allocs/op
|
||||
BenchmarkHttpRouter_Param5 5000000 403 ns/op 160 B/op 1 allocs/op
|
||||
BenchmarkHttpTreeMux_Param5 1000000 1089 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkKocha_Param5 1000000 1682 ns/op 440 B/op 10 allocs/op
|
||||
BenchmarkMacaron_Param5 300000 4596 ns/op 1376 B/op 14 allocs/op
|
||||
BenchmarkMartini_Param5 100000 15703 ns/op 1280 B/op 12 allocs/op
|
||||
BenchmarkPat_Param5 300000 5320 ns/op 1008 B/op 42 allocs/op
|
||||
BenchmarkPossum_Param5 1000000 2155 ns/op 624 B/op 7 allocs/op
|
||||
BenchmarkR2router_Param5 1000000 1559 ns/op 432 B/op 6 allocs/op
|
||||
BenchmarkRevel_Param5 200000 8184 ns/op 2024 B/op 35 allocs/op
|
||||
BenchmarkRivet_Param5 1000000 1914 ns/op 528 B/op 9 allocs/op
|
||||
BenchmarkTango_Param5 1000000 3280 ns/op 944 B/op 18 allocs/op
|
||||
BenchmarkTigerTonic_Param5 200000 11638 ns/op 2519 B/op 53 allocs/op
|
||||
BenchmarkTraffic_Param5 200000 8941 ns/op 2280 B/op 31 allocs/op
|
||||
BenchmarkVulcan_Param5 1000000 1279 ns/op 98 B/op 3 allocs/op
|
||||
BenchmarkZeus_Param5 1000000 1574 ns/op 416 B/op 3 allocs/op
|
||||
BenchmarkAce_Param20 1000000 1528 ns/op 640 B/op 1 allocs/op
|
||||
BenchmarkBear_Param20 300000 4906 ns/op 1633 B/op 5 allocs/op
|
||||
BenchmarkBeego_Param20 200000 10529 ns/op 3868 B/op 17 allocs/op
|
||||
BenchmarkBone_Param20 300000 7362 ns/op 2539 B/op 5 allocs/op
|
||||
BenchmarkDenco_Param20 1000000 1884 ns/op 640 B/op 1 allocs/op
|
||||
BenchmarkEcho_Param20 2000000 689 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_Param20 3000000 545 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_Param20 200000 9437 ns/op 3804 B/op 16 allocs/op
|
||||
BenchmarkGoji_Param20 500000 3987 ns/op 1246 B/op 2 allocs/op
|
||||
BenchmarkGoJsonRest_Param20 100000 12799 ns/op 4492 B/op 21 allocs/op
|
||||
BenchmarkGoRestful_Param20 100000 19451 ns/op 5244 B/op 33 allocs/op
|
||||
BenchmarkGorillaMux_Param20 100000 12456 ns/op 3275 B/op 11 allocs/op
|
||||
BenchmarkHttpRouter_Param20 1000000 1333 ns/op 640 B/op 1 allocs/op
|
||||
BenchmarkHttpTreeMux_Param20 300000 6490 ns/op 2187 B/op 4 allocs/op
|
||||
BenchmarkKocha_Param20 300000 5335 ns/op 1808 B/op 27 allocs/op
|
||||
BenchmarkMacaron_Param20 200000 11325 ns/op 4252 B/op 18 allocs/op
|
||||
BenchmarkMartini_Param20 20000 64419 ns/op 3644 B/op 14 allocs/op
|
||||
BenchmarkPat_Param20 50000 24672 ns/op 4888 B/op 151 allocs/op
|
||||
BenchmarkPossum_Param20 1000000 2085 ns/op 624 B/op 7 allocs/op
|
||||
BenchmarkR2router_Param20 300000 6809 ns/op 2283 B/op 8 allocs/op
|
||||
BenchmarkRevel_Param20 100000 16600 ns/op 5551 B/op 54 allocs/op
|
||||
BenchmarkRivet_Param20 200000 8428 ns/op 2620 B/op 26 allocs/op
|
||||
BenchmarkTango_Param20 100000 16302 ns/op 8224 B/op 48 allocs/op
|
||||
BenchmarkTigerTonic_Param20 30000 46828 ns/op 10538 B/op 178 allocs/op
|
||||
BenchmarkTraffic_Param20 50000 28871 ns/op 7998 B/op 66 allocs/op
|
||||
BenchmarkVulcan_Param20 1000000 2267 ns/op 98 B/op 3 allocs/op
|
||||
BenchmarkZeus_Param20 300000 6828 ns/op 2507 B/op 5 allocs/op
|
||||
BenchmarkAce_ParamWrite 3000000 502 ns/op 40 B/op 2 allocs/op
|
||||
BenchmarkBear_ParamWrite 1000000 1303 ns/op 424 B/op 5 allocs/op
|
||||
BenchmarkBeego_ParamWrite 1000000 2489 ns/op 728 B/op 11 allocs/op
|
||||
BenchmarkBone_ParamWrite 1000000 1181 ns/op 384 B/op 3 allocs/op
|
||||
BenchmarkDenco_ParamWrite 5000000 315 ns/op 32 B/op 1 allocs/op
|
||||
BenchmarkEcho_ParamWrite 10000000 237 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkGin_ParamWrite 5000000 336 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_ParamWrite 1000000 2079 ns/op 664 B/op 10 allocs/op
|
||||
BenchmarkGoji_ParamWrite 1000000 1092 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkGoJsonRest_ParamWrite 1000000 3329 ns/op 1136 B/op 19 allocs/op
|
||||
BenchmarkGoRestful_ParamWrite 200000 9273 ns/op 2504 B/op 32 allocs/op
|
||||
BenchmarkGorillaMux_ParamWrite 500000 3919 ns/op 792 B/op 10 allocs/op
|
||||
BenchmarkHttpRouter_ParamWrite 10000000 223 ns/op 32 B/op 1 allocs/op
|
||||
BenchmarkHttpTreeMux_ParamWrite 2000000 788 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkKocha_ParamWrite 3000000 549 ns/op 56 B/op 3 allocs/op
|
||||
BenchmarkMacaron_ParamWrite 500000 4558 ns/op 1216 B/op 16 allocs/op
|
||||
BenchmarkMartini_ParamWrite 200000 8850 ns/op 1256 B/op 16 allocs/op
|
||||
BenchmarkPat_ParamWrite 500000 3679 ns/op 1088 B/op 19 allocs/op
|
||||
BenchmarkPossum_ParamWrite 1000000 2114 ns/op 624 B/op 7 allocs/op
|
||||
BenchmarkR2router_ParamWrite 1000000 1320 ns/op 432 B/op 6 allocs/op
|
||||
BenchmarkRevel_ParamWrite 200000 8048 ns/op 2128 B/op 33 allocs/op
|
||||
BenchmarkRivet_ParamWrite 1000000 1393 ns/op 472 B/op 6 allocs/op
|
||||
BenchmarkTango_ParamWrite 2000000 819 ns/op 136 B/op 5 allocs/op
|
||||
BenchmarkTigerTonic_ParamWrite 300000 5860 ns/op 1440 B/op 25 allocs/op
|
||||
BenchmarkTraffic_ParamWrite 200000 7429 ns/op 2400 B/op 27 allocs/op
|
||||
BenchmarkVulcan_ParamWrite 2000000 972 ns/op 98 B/op 3 allocs/op
|
||||
BenchmarkZeus_ParamWrite 1000000 1226 ns/op 368 B/op 3 allocs/op
|
||||
BenchmarkAce_GithubStatic 5000000 294 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkBear_GithubStatic 3000000 575 ns/op 88 B/op 3 allocs/op
|
||||
BenchmarkBeego_GithubStatic 1000000 1561 ns/op 368 B/op 7 allocs/op
|
||||
BenchmarkBone_GithubStatic 200000 12301 ns/op 2880 B/op 60 allocs/op
|
||||
BenchmarkDenco_GithubStatic 20000000 74.6 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkEcho_GithubStatic 10000000 176 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_GithubStatic 10000000 159 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_GithubStatic 1000000 1116 ns/op 304 B/op 6 allocs/op
|
||||
BenchmarkGoji_GithubStatic 5000000 413 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGoRestful_GithubStatic 30000 55200 ns/op 3520 B/op 36 allocs/op
|
||||
BenchmarkGoJsonRest_GithubStatic 1000000 1504 ns/op 337 B/op 12 allocs/op
|
||||
BenchmarkGorillaMux_GithubStatic 100000 23620 ns/op 464 B/op 8 allocs/op
|
||||
BenchmarkHttpRouter_GithubStatic 20000000 78.3 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkHttpTreeMux_GithubStatic 20000000 84.9 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkKocha_GithubStatic 20000000 111 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMacaron_GithubStatic 1000000 2686 ns/op 752 B/op 8 allocs/op
|
||||
BenchmarkMartini_GithubStatic 100000 22244 ns/op 832 B/op 11 allocs/op
|
||||
BenchmarkPat_GithubStatic 100000 13278 ns/op 3648 B/op 76 allocs/op
|
||||
BenchmarkPossum_GithubStatic 1000000 1429 ns/op 480 B/op 4 allocs/op
|
||||
BenchmarkR2router_GithubStatic 2000000 726 ns/op 144 B/op 5 allocs/op
|
||||
BenchmarkRevel_GithubStatic 300000 6271 ns/op 1288 B/op 25 allocs/op
|
||||
BenchmarkRivet_GithubStatic 3000000 474 ns/op 112 B/op 2 allocs/op
|
||||
BenchmarkTango_GithubStatic 1000000 1842 ns/op 256 B/op 10 allocs/op
|
||||
BenchmarkTigerTonic_GithubStatic 5000000 361 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkTraffic_GithubStatic 30000 47197 ns/op 18920 B/op 149 allocs/op
|
||||
BenchmarkVulcan_GithubStatic 1000000 1415 ns/op 98 B/op 3 allocs/op
|
||||
BenchmarkZeus_GithubStatic 1000000 2522 ns/op 512 B/op 11 allocs/op
|
||||
BenchmarkAce_GithubParam 3000000 578 ns/op 96 B/op 1 allocs/op
|
||||
BenchmarkBear_GithubParam 1000000 1592 ns/op 464 B/op 5 allocs/op
|
||||
BenchmarkBeego_GithubParam 1000000 2891 ns/op 784 B/op 11 allocs/op
|
||||
BenchmarkBone_GithubParam 300000 6440 ns/op 1456 B/op 16 allocs/op
|
||||
BenchmarkDenco_GithubParam 3000000 514 ns/op 128 B/op 1 allocs/op
|
||||
BenchmarkEcho_GithubParam 5000000 292 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_GithubParam 10000000 242 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_GithubParam 1000000 2343 ns/op 720 B/op 10 allocs/op
|
||||
BenchmarkGoji_GithubParam 1000000 1566 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkGoJsonRest_GithubParam 1000000 2828 ns/op 721 B/op 15 allocs/op
|
||||
BenchmarkGoRestful_GithubParam 10000 177711 ns/op 2816 B/op 35 allocs/op
|
||||
BenchmarkGorillaMux_GithubParam 100000 13591 ns/op 816 B/op 9 allocs/op
|
||||
BenchmarkHttpRouter_GithubParam 5000000 352 ns/op 96 B/op 1 allocs/op
|
||||
BenchmarkHttpTreeMux_GithubParam 2000000 973 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkKocha_GithubParam 2000000 889 ns/op 128 B/op 5 allocs/op
|
||||
BenchmarkMacaron_GithubParam 500000 4047 ns/op 1168 B/op 12 allocs/op
|
||||
BenchmarkMartini_GithubParam 50000 28982 ns/op 1184 B/op 12 allocs/op
|
||||
BenchmarkPat_GithubParam 200000 8747 ns/op 2480 B/op 56 allocs/op
|
||||
BenchmarkPossum_GithubParam 1000000 2158 ns/op 624 B/op 7 allocs/op
|
||||
BenchmarkR2router_GithubParam 1000000 1352 ns/op 432 B/op 6 allocs/op
|
||||
BenchmarkRevel_GithubParam 200000 7673 ns/op 1784 B/op 30 allocs/op
|
||||
BenchmarkRivet_GithubParam 1000000 1573 ns/op 480 B/op 6 allocs/op
|
||||
BenchmarkTango_GithubParam 1000000 2418 ns/op 480 B/op 13 allocs/op
|
||||
BenchmarkTigerTonic_GithubParam 300000 6048 ns/op 1440 B/op 28 allocs/op
|
||||
BenchmarkTraffic_GithubParam 100000 20143 ns/op 6024 B/op 55 allocs/op
|
||||
BenchmarkVulcan_GithubParam 1000000 2224 ns/op 98 B/op 3 allocs/op
|
||||
BenchmarkZeus_GithubParam 500000 4156 ns/op 1312 B/op 12 allocs/op
|
||||
BenchmarkAce_GithubAll 10000 109482 ns/op 13792 B/op 167 allocs/op
|
||||
BenchmarkBear_GithubAll 10000 287490 ns/op 79952 B/op 943 allocs/op
|
||||
BenchmarkBeego_GithubAll 3000 562184 ns/op 146272 B/op 2092 allocs/op
|
||||
BenchmarkBone_GithubAll 500 2578716 ns/op 648016 B/op 8119 allocs/op
|
||||
BenchmarkDenco_GithubAll 20000 94955 ns/op 20224 B/op 167 allocs/op
|
||||
BenchmarkEcho_GithubAll 30000 58705 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_GithubAll 30000 50991 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_GithubAll 5000 449648 ns/op 133280 B/op 1889 allocs/op
|
||||
BenchmarkGoji_GithubAll 2000 689748 ns/op 56113 B/op 334 allocs/op
|
||||
BenchmarkGoJsonRest_GithubAll 5000 537769 ns/op 135995 B/op 2940 allocs/op
|
||||
BenchmarkGoRestful_GithubAll 100 18410628 ns/op 797236 B/op 7725 allocs/op
|
||||
BenchmarkGorillaMux_GithubAll 200 8036360 ns/op 153137 B/op 1791 allocs/op
|
||||
BenchmarkHttpRouter_GithubAll 20000 63506 ns/op 13792 B/op 167 allocs/op
|
||||
BenchmarkHttpTreeMux_GithubAll 10000 165927 ns/op 56112 B/op 334 allocs/op
|
||||
BenchmarkKocha_GithubAll 10000 171362 ns/op 23304 B/op 843 allocs/op
|
||||
BenchmarkMacaron_GithubAll 2000 817008 ns/op 224960 B/op 2315 allocs/op
|
||||
BenchmarkMartini_GithubAll 100 12609209 ns/op 237952 B/op 2686 allocs/op
|
||||
BenchmarkPat_GithubAll 300 4830398 ns/op 1504101 B/op 32222 allocs/op
|
||||
BenchmarkPossum_GithubAll 10000 301716 ns/op 97440 B/op 812 allocs/op
|
||||
BenchmarkR2router_GithubAll 10000 270691 ns/op 77328 B/op 1182 allocs/op
|
||||
BenchmarkRevel_GithubAll 1000 1491919 ns/op 345553 B/op 5918 allocs/op
|
||||
BenchmarkRivet_GithubAll 10000 283860 ns/op 84272 B/op 1079 allocs/op
|
||||
BenchmarkTango_GithubAll 5000 473821 ns/op 87078 B/op 2470 allocs/op
|
||||
BenchmarkTigerTonic_GithubAll 2000 1120131 ns/op 241088 B/op 6052 allocs/op
|
||||
BenchmarkTraffic_GithubAll 200 8708979 ns/op 2664762 B/op 22390 allocs/op
|
||||
BenchmarkVulcan_GithubAll 5000 353392 ns/op 19894 B/op 609 allocs/op
|
||||
BenchmarkZeus_GithubAll 2000 944234 ns/op 300688 B/op 2648 allocs/op
|
||||
BenchmarkAce_GPlusStatic 5000000 251 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkBear_GPlusStatic 3000000 415 ns/op 72 B/op 3 allocs/op
|
||||
BenchmarkBeego_GPlusStatic 1000000 1416 ns/op 352 B/op 7 allocs/op
|
||||
BenchmarkBone_GPlusStatic 10000000 192 ns/op 32 B/op 1 allocs/op
|
||||
BenchmarkDenco_GPlusStatic 30000000 47.6 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkEcho_GPlusStatic 10000000 131 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_GPlusStatic 10000000 131 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_GPlusStatic 1000000 1035 ns/op 288 B/op 6 allocs/op
|
||||
BenchmarkGoji_GPlusStatic 5000000 304 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGoJsonRest_GPlusStatic 1000000 1286 ns/op 337 B/op 12 allocs/op
|
||||
BenchmarkGoRestful_GPlusStatic 200000 9649 ns/op 2160 B/op 30 allocs/op
|
||||
BenchmarkGorillaMux_GPlusStatic 1000000 2346 ns/op 464 B/op 8 allocs/op
|
||||
BenchmarkHttpRouter_GPlusStatic 30000000 42.7 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkHttpTreeMux_GPlusStatic 30000000 49.5 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkKocha_GPlusStatic 20000000 74.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMacaron_GPlusStatic 1000000 2520 ns/op 736 B/op 8 allocs/op
|
||||
BenchmarkMartini_GPlusStatic 300000 5310 ns/op 832 B/op 11 allocs/op
|
||||
BenchmarkPat_GPlusStatic 5000000 398 ns/op 96 B/op 2 allocs/op
|
||||
BenchmarkPossum_GPlusStatic 1000000 1434 ns/op 480 B/op 4 allocs/op
|
||||
BenchmarkR2router_GPlusStatic 2000000 646 ns/op 144 B/op 5 allocs/op
|
||||
BenchmarkRevel_GPlusStatic 300000 6172 ns/op 1272 B/op 25 allocs/op
|
||||
BenchmarkRivet_GPlusStatic 3000000 444 ns/op 112 B/op 2 allocs/op
|
||||
BenchmarkTango_GPlusStatic 1000000 1400 ns/op 208 B/op 10 allocs/op
|
||||
BenchmarkTigerTonic_GPlusStatic 10000000 213 ns/op 32 B/op 1 allocs/op
|
||||
BenchmarkTraffic_GPlusStatic 1000000 3091 ns/op 1208 B/op 16 allocs/op
|
||||
BenchmarkVulcan_GPlusStatic 2000000 863 ns/op 98 B/op 3 allocs/op
|
||||
BenchmarkZeus_GPlusStatic 10000000 237 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkAce_GPlusParam 3000000 435 ns/op 64 B/op 1 allocs/op
|
||||
BenchmarkBear_GPlusParam 1000000 1205 ns/op 448 B/op 5 allocs/op
|
||||
BenchmarkBeego_GPlusParam 1000000 2494 ns/op 720 B/op 10 allocs/op
|
||||
BenchmarkBone_GPlusParam 1000000 1126 ns/op 384 B/op 3 allocs/op
|
||||
BenchmarkDenco_GPlusParam 5000000 325 ns/op 64 B/op 1 allocs/op
|
||||
BenchmarkEcho_GPlusParam 10000000 168 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_GPlusParam 10000000 170 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_GPlusParam 1000000 1895 ns/op 656 B/op 9 allocs/op
|
||||
BenchmarkGoji_GPlusParam 1000000 1071 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkGoJsonRest_GPlusParam 1000000 2282 ns/op 657 B/op 14 allocs/op
|
||||
BenchmarkGoRestful_GPlusParam 100000 19400 ns/op 2560 B/op 33 allocs/op
|
||||
BenchmarkGorillaMux_GPlusParam 500000 5001 ns/op 784 B/op 9 allocs/op
|
||||
BenchmarkHttpRouter_GPlusParam 10000000 240 ns/op 64 B/op 1 allocs/op
|
||||
BenchmarkHttpTreeMux_GPlusParam 2000000 797 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkKocha_GPlusParam 3000000 505 ns/op 56 B/op 3 allocs/op
|
||||
BenchmarkMacaron_GPlusParam 1000000 3668 ns/op 1104 B/op 11 allocs/op
|
||||
BenchmarkMartini_GPlusParam 200000 10672 ns/op 1152 B/op 12 allocs/op
|
||||
BenchmarkPat_GPlusParam 1000000 2376 ns/op 704 B/op 14 allocs/op
|
||||
BenchmarkPossum_GPlusParam 1000000 2090 ns/op 624 B/op 7 allocs/op
|
||||
BenchmarkR2router_GPlusParam 1000000 1233 ns/op 432 B/op 6 allocs/op
|
||||
BenchmarkRevel_GPlusParam 200000 6778 ns/op 1704 B/op 28 allocs/op
|
||||
BenchmarkRivet_GPlusParam 1000000 1279 ns/op 464 B/op 5 allocs/op
|
||||
BenchmarkTango_GPlusParam 1000000 1981 ns/op 272 B/op 10 allocs/op
|
||||
BenchmarkTigerTonic_GPlusParam 500000 3893 ns/op 1064 B/op 19 allocs/op
|
||||
BenchmarkTraffic_GPlusParam 200000 6585 ns/op 2000 B/op 23 allocs/op
|
||||
BenchmarkVulcan_GPlusParam 1000000 1233 ns/op 98 B/op 3 allocs/op
|
||||
BenchmarkZeus_GPlusParam 1000000 1350 ns/op 368 B/op 3 allocs/op
|
||||
BenchmarkAce_GPlus2Params 3000000 512 ns/op 64 B/op 1 allocs/op
|
||||
BenchmarkBear_GPlus2Params 1000000 1564 ns/op 464 B/op 5 allocs/op
|
||||
BenchmarkBeego_GPlus2Params 1000000 3043 ns/op 784 B/op 11 allocs/op
|
||||
BenchmarkBone_GPlus2Params 1000000 3152 ns/op 736 B/op 7 allocs/op
|
||||
BenchmarkDenco_GPlus2Params 3000000 431 ns/op 64 B/op 1 allocs/op
|
||||
BenchmarkEcho_GPlus2Params 5000000 247 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_GPlus2Params 10000000 219 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_GPlus2Params 1000000 2363 ns/op 720 B/op 10 allocs/op
|
||||
BenchmarkGoji_GPlus2Params 1000000 1540 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkGoJsonRest_GPlus2Params 1000000 2872 ns/op 721 B/op 15 allocs/op
|
||||
BenchmarkGoRestful_GPlus2Params 100000 23030 ns/op 2720 B/op 35 allocs/op
|
||||
BenchmarkGorillaMux_GPlus2Params 200000 10516 ns/op 816 B/op 9 allocs/op
|
||||
BenchmarkHttpRouter_GPlus2Params 5000000 273 ns/op 64 B/op 1 allocs/op
|
||||
BenchmarkHttpTreeMux_GPlus2Params 2000000 939 ns/op 336 B/op 2 allocs/op
|
||||
BenchmarkKocha_GPlus2Params 2000000 844 ns/op 128 B/op 5 allocs/op
|
||||
BenchmarkMacaron_GPlus2Params 500000 3914 ns/op 1168 B/op 12 allocs/op
|
||||
BenchmarkMartini_GPlus2Params 50000 35759 ns/op 1280 B/op 16 allocs/op
|
||||
BenchmarkPat_GPlus2Params 200000 7089 ns/op 2304 B/op 41 allocs/op
|
||||
BenchmarkPossum_GPlus2Params 1000000 2093 ns/op 624 B/op 7 allocs/op
|
||||
BenchmarkR2router_GPlus2Params 1000000 1320 ns/op 432 B/op 6 allocs/op
|
||||
BenchmarkRevel_GPlus2Params 200000 7351 ns/op 1800 B/op 30 allocs/op
|
||||
BenchmarkRivet_GPlus2Params 1000000 1485 ns/op 480 B/op 6 allocs/op
|
||||
BenchmarkTango_GPlus2Params 1000000 2111 ns/op 448 B/op 12 allocs/op
|
||||
BenchmarkTigerTonic_GPlus2Params 300000 6271 ns/op 1528 B/op 28 allocs/op
|
||||
BenchmarkTraffic_GPlus2Params 100000 14886 ns/op 3312 B/op 34 allocs/op
|
||||
BenchmarkVulcan_GPlus2Params 1000000 1883 ns/op 98 B/op 3 allocs/op
|
||||
BenchmarkZeus_GPlus2Params 1000000 2686 ns/op 784 B/op 6 allocs/op
|
||||
BenchmarkAce_GPlusAll 300000 5912 ns/op 640 B/op 11 allocs/op
|
||||
BenchmarkBear_GPlusAll 100000 16448 ns/op 5072 B/op 61 allocs/op
|
||||
BenchmarkBeego_GPlusAll 50000 32916 ns/op 8976 B/op 129 allocs/op
|
||||
BenchmarkBone_GPlusAll 50000 25836 ns/op 6992 B/op 76 allocs/op
|
||||
BenchmarkDenco_GPlusAll 500000 4462 ns/op 672 B/op 11 allocs/op
|
||||
BenchmarkEcho_GPlusAll 500000 2806 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGin_GPlusAll 500000 2579 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGocraftWeb_GPlusAll 50000 25223 ns/op 8144 B/op 116 allocs/op
|
||||
BenchmarkGoji_GPlusAll 100000 14237 ns/op 3696 B/op 22 allocs/op
|
||||
BenchmarkGoJsonRest_GPlusAll 50000 29227 ns/op 8221 B/op 183 allocs/op
|
||||
BenchmarkGoRestful_GPlusAll 10000 203144 ns/op 36064 B/op 441 allocs/op
|
||||
BenchmarkGorillaMux_GPlusAll 20000 80906 ns/op 9712 B/op 115 allocs/op
|
||||
BenchmarkHttpRouter_GPlusAll 500000 3040 ns/op 640 B/op 11 allocs/op
|
||||
BenchmarkHttpTreeMux_GPlusAll 200000 9627 ns/op 3696 B/op 22 allocs/op
|
||||
BenchmarkKocha_GPlusAll 200000 8108 ns/op 976 B/op 43 allocs/op
|
||||
BenchmarkMacaron_GPlusAll 30000 48083 ns/op 13968 B/op 142 allocs/op
|
||||
BenchmarkMartini_GPlusAll 10000 196978 ns/op 15072 B/op 178 allocs/op
|
||||
BenchmarkPat_GPlusAll 30000 58865 ns/op 16880 B/op 343 allocs/op
|
||||
BenchmarkPossum_GPlusAll 100000 19685 ns/op 6240 B/op 52 allocs/op
|
||||
BenchmarkR2router_GPlusAll 100000 16251 ns/op 5040 B/op 76 allocs/op
|
||||
BenchmarkRevel_GPlusAll 20000 93489 ns/op 21656 B/op 368 allocs/op
|
||||
BenchmarkRivet_GPlusAll 100000 16907 ns/op 5408 B/op 64 allocs/op
|
||||
```
|
150
Godeps/_workspace/src/github.com/gin-gonic/gin/CHANGELOG.md
generated
vendored
150
Godeps/_workspace/src/github.com/gin-gonic/gin/CHANGELOG.md
generated
vendored
@ -1,150 +0,0 @@
|
||||
#CHANGELOG
|
||||
|
||||
###Gin 1.0rc2 (...)
|
||||
|
||||
- [PERFORMANCE] Fast path for writing Content-Type.
|
||||
- [PERFORMANCE] Much faster 404 routing
|
||||
- [PERFORMANCE] Allocation optimizations
|
||||
- [PERFORMANCE] Faster root tree lookup
|
||||
- [PERFORMANCE] Zero overhead, String() and JSON() rendering.
|
||||
- [PERFORMANCE] Faster ClientIP parsing
|
||||
- [PERFORMANCE] Much faster SSE implementation
|
||||
- [NEW] Benchmarks suite
|
||||
- [NEW] Bind validation can be disabled and replaced with custom validators.
|
||||
- [NEW] More flexible HTML render
|
||||
- [NEW] Multipart and PostForm bindings
|
||||
- [NEW] Adds method to return all the registered routes
|
||||
- [NEW] Context.HandlerName() returns the main handler's name
|
||||
- [NEW] Adds Error.IsType() helper
|
||||
- [FIX] Binding multipart form
|
||||
- [FIX] Integration tests
|
||||
- [FIX] Crash when binding non struct object in Context.
|
||||
- [FIX] RunTLS() implementation
|
||||
- [FIX] Logger() unit tests
|
||||
- [FIX] Adds SetHTMLTemplate() warning
|
||||
- [FIX] Context.IsAborted()
|
||||
- [FIX] More unit tests
|
||||
- [FIX] JSON, XML, HTML renders accept custom content-types
|
||||
- [FIX] gin.AbortIndex is unexported
|
||||
- [FIX] Better approach to avoid directory listing in StaticFS()
|
||||
- [FIX] Context.ClientIP() always returns the IP with trimmed spaces.
|
||||
- [FIX] Better warning when running in debug mode.
|
||||
- [FIX] Google App Engine integration. debugPrint does not use os.Stdout
|
||||
- [FIX] Fixes integer overflow in error type
|
||||
- [FIX] Error implements the json.Marshaller interface
|
||||
- [FIX] MIT license in every file
|
||||
|
||||
|
||||
###Gin 1.0rc1 (May 22, 2015)
|
||||
|
||||
- [PERFORMANCE] Zero allocation router
|
||||
- [PERFORMANCE] Faster JSON, XML and text rendering
|
||||
- [PERFORMANCE] Custom hand optimized HttpRouter for Gin
|
||||
- [PERFORMANCE] Misc code optimizations. Inlining, tail call optimizations
|
||||
- [NEW] Built-in support for golang.org/x/net/context
|
||||
- [NEW] Any(path, handler). Create a route that matches any path
|
||||
- [NEW] Refactored rendering pipeline (faster and static typeded)
|
||||
- [NEW] Refactored errors API
|
||||
- [NEW] IndentedJSON() prints pretty JSON
|
||||
- [NEW] Added gin.DefaultWriter
|
||||
- [NEW] UNIX socket support
|
||||
- [NEW] RouterGroup.BasePath is exposed
|
||||
- [NEW] JSON validation using go-validate-yourself (very powerful options)
|
||||
- [NEW] Completed suite of unit tests
|
||||
- [NEW] HTTP streaming with c.Stream()
|
||||
- [NEW] StaticFile() creates a router for serving just one file.
|
||||
- [NEW] StaticFS() has an option to disable directory listing.
|
||||
- [NEW] StaticFS() for serving static files through virtual filesystems
|
||||
- [NEW] Server-Sent Events native support
|
||||
- [NEW] WrapF() and WrapH() helpers for wrapping http.HandlerFunc and http.Handler
|
||||
- [NEW] Added LoggerWithWriter() middleware
|
||||
- [NEW] Added RecoveryWithWriter() middleware
|
||||
- [NEW] Added DefaultPostFormValue()
|
||||
- [NEW] Added DefaultFormValue()
|
||||
- [NEW] Added DefaultParamValue()
|
||||
- [FIX] BasicAuth() when using custom realm
|
||||
- [FIX] Bug when serving static files in nested routing group
|
||||
- [FIX] Redirect using built-in http.Redirect()
|
||||
- [FIX] Logger when printing the requested path
|
||||
- [FIX] Documentation typos
|
||||
- [FIX] Context.Engine renamed to Context.engine
|
||||
- [FIX] Better debugging messages
|
||||
- [FIX] ErrorLogger
|
||||
- [FIX] Debug HTTP render
|
||||
- [FIX] Refactored binding and render modules
|
||||
- [FIX] Refactored Context initialization
|
||||
- [FIX] Refactored BasicAuth()
|
||||
- [FIX] NoMethod/NoRoute handlers
|
||||
- [FIX] Hijacking http
|
||||
- [FIX] Better support for Google App Engine (using log instead of fmt)
|
||||
|
||||
|
||||
###Gin 0.6 (Mar 9, 2015)
|
||||
|
||||
- [NEW] Support multipart/form-data
|
||||
- [NEW] NoMethod handler
|
||||
- [NEW] Validate sub structures
|
||||
- [NEW] Support for HTTP Realm Auth
|
||||
- [FIX] Unsigned integers in binding
|
||||
- [FIX] Improve color logger
|
||||
|
||||
|
||||
###Gin 0.5 (Feb 7, 2015)
|
||||
|
||||
- [NEW] Content Negotiation
|
||||
- [FIX] Solved security bug that allow a client to spoof ip
|
||||
- [FIX] Fix unexported/ignored fields in binding
|
||||
|
||||
|
||||
###Gin 0.4 (Aug 21, 2014)
|
||||
|
||||
- [NEW] Development mode
|
||||
- [NEW] Unit tests
|
||||
- [NEW] Add Content.Redirect()
|
||||
- [FIX] Deferring WriteHeader()
|
||||
- [FIX] Improved documentation for model binding
|
||||
|
||||
|
||||
###Gin 0.3 (Jul 18, 2014)
|
||||
|
||||
- [PERFORMANCE] Normal log and error log are printed in the same call.
|
||||
- [PERFORMANCE] Improve performance of NoRouter()
|
||||
- [PERFORMANCE] Improve context's memory locality, reduce CPU cache faults.
|
||||
- [NEW] Flexible rendering API
|
||||
- [NEW] Add Context.File()
|
||||
- [NEW] Add shorcut RunTLS() for http.ListenAndServeTLS
|
||||
- [FIX] Rename NotFound404() to NoRoute()
|
||||
- [FIX] Errors in context are purged
|
||||
- [FIX] Adds HEAD method in Static file serving
|
||||
- [FIX] Refactors Static() file serving
|
||||
- [FIX] Using keyed initialization to fix app-engine integration
|
||||
- [FIX] Can't unmarshal JSON array, #63
|
||||
- [FIX] Renaming Context.Req to Context.Request
|
||||
- [FIX] Check application/x-www-form-urlencoded when parsing form
|
||||
|
||||
|
||||
###Gin 0.2b (Jul 08, 2014)
|
||||
- [PERFORMANCE] Using sync.Pool to allocatio/gc overhead
|
||||
- [NEW] Travis CI integration
|
||||
- [NEW] Completely new logger
|
||||
- [NEW] New API for serving static files. gin.Static()
|
||||
- [NEW] gin.H() can be serialized into XML
|
||||
- [NEW] Typed errors. Errors can be typed. Internet/external/custom.
|
||||
- [NEW] Support for Godeps
|
||||
- [NEW] Travis/Godocs badges in README
|
||||
- [NEW] New Bind() and BindWith() methods for parsing request body.
|
||||
- [NEW] Add Content.Copy()
|
||||
- [NEW] Add context.LastError()
|
||||
- [NEW] Add shorcut for OPTIONS HTTP method
|
||||
- [FIX] Tons of README fixes
|
||||
- [FIX] Header is written before body
|
||||
- [FIX] BasicAuth() and changes API a little bit
|
||||
- [FIX] Recovery() middleware only prints panics
|
||||
- [FIX] Context.Get() does not panic anymore. Use MustGet() instead.
|
||||
- [FIX] Multiple http.WriteHeader() in NotFound handlers
|
||||
- [FIX] Engine.Run() panics if http server can't be setted up
|
||||
- [FIX] Crash when route path doesn't start with '/'
|
||||
- [FIX] Do not update header when status code is negative
|
||||
- [FIX] Setting response headers before calling WriteHeader in context.String()
|
||||
- [FIX] Add MIT license
|
||||
- [FIX] Changes behaviour of ErrorLogger() and Logger()
|
21
Godeps/_workspace/src/github.com/gin-gonic/gin/LICENSE
generated
vendored
21
Godeps/_workspace/src/github.com/gin-gonic/gin/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Manuel Martínez-Almeida
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
710
Godeps/_workspace/src/github.com/gin-gonic/gin/README.md
generated
vendored
710
Godeps/_workspace/src/github.com/gin-gonic/gin/README.md
generated
vendored
@ -1,710 +0,0 @@
|
||||
|
||||
#Gin Web Framework
|
||||
<img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.jpg">
|
||||
[](https://travis-ci.org/gin-gonic/gin)
|
||||
[](https://coveralls.io/r/gin-gonic/gin?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/gin-gonic/gin)
|
||||
[](https://godoc.org/github.com/gin-gonic/gin)
|
||||
[](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
```sh
|
||||
$ cat test.go
|
||||
```
|
||||
```go
|
||||
package main
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{
|
||||
"message": "pong",
|
||||
})
|
||||
})
|
||||
r.Run() // listen and server on 0.0.0.0:8080
|
||||
}
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter)
|
||||
|
||||
[See all benchmarks](/BENCHMARKS.md)
|
||||
|
||||
|
||||
Benchmark name | (1) | (2) | (3) | (4)
|
||||
--------------------------------|----------:|----------:|----------:|------:
|
||||
BenchmarkAce_GithubAll | 10000 | 109482 | 13792 | 167
|
||||
BenchmarkBear_GithubAll | 10000 | 287490 | 79952 | 943
|
||||
BenchmarkBeego_GithubAll | 3000 | 562184 | 146272 | 2092
|
||||
BenchmarkBone_GithubAll | 500 | 2578716 | 648016 | 8119
|
||||
BenchmarkDenco_GithubAll | 20000 | 94955 | 20224 | 167
|
||||
BenchmarkEcho_GithubAll | 30000 | 58705 | 0 | 0
|
||||
**BenchmarkGin_GithubAll** | **30000** | **50991** | **0** | **0**
|
||||
BenchmarkGocraftWeb_GithubAll | 5000 | 449648 | 133280 | 1889
|
||||
BenchmarkGoji_GithubAll | 2000 | 689748 | 56113 | 334
|
||||
BenchmarkGoJsonRest_GithubAll | 5000 | 537769 | 135995 | 2940
|
||||
BenchmarkGoRestful_GithubAll | 100 | 18410628 | 797236 | 7725
|
||||
BenchmarkGorillaMux_GithubAll | 200 | 8036360 | 153137 | 1791
|
||||
BenchmarkHttpRouter_GithubAll | 20000 | 63506 | 13792 | 167
|
||||
BenchmarkHttpTreeMux_GithubAll | 10000 | 165927 | 56112 | 334
|
||||
BenchmarkKocha_GithubAll | 10000 | 171362 | 23304 | 843
|
||||
BenchmarkMacaron_GithubAll | 2000 | 817008 | 224960 | 2315
|
||||
BenchmarkMartini_GithubAll | 100 | 12609209 | 237952 | 2686
|
||||
BenchmarkPat_GithubAll | 300 | 4830398 | 1504101 | 32222
|
||||
BenchmarkPossum_GithubAll | 10000 | 301716 | 97440 | 812
|
||||
BenchmarkR2router_GithubAll | 10000 | 270691 | 77328 | 1182
|
||||
BenchmarkRevel_GithubAll | 1000 | 1491919 | 345553 | 5918
|
||||
BenchmarkRivet_GithubAll | 10000 | 283860 | 84272 | 1079
|
||||
BenchmarkTango_GithubAll | 5000 | 473821 | 87078 | 2470
|
||||
BenchmarkTigerTonic_GithubAll | 2000 | 1120131 | 241088 | 6052
|
||||
BenchmarkTraffic_GithubAll | 200 | 8708979 | 2664762 | 22390
|
||||
BenchmarkVulcan_GithubAll | 5000 | 353392 | 19894 | 609
|
||||
BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648
|
||||
|
||||
(1): Total Repetitions
|
||||
(2): Single Repetition Duration (ns/op)
|
||||
(3): Heap Memory (B/op)
|
||||
(4): Average Allocations per Repetition (allocs/op)
|
||||
|
||||
##Gin v1. stable
|
||||
|
||||
- [x] Zero allocation router.
|
||||
- [x] Still the fastest http router and framework. From routing to writing.
|
||||
- [x] Complete suite of unit tests
|
||||
- [x] Battle tested
|
||||
- [x] API frozen, new releases will not break your code.
|
||||
|
||||
|
||||
## Start using it
|
||||
1. Download and install it:
|
||||
|
||||
```sh
|
||||
$ go get github.com/gin-gonic/gin
|
||||
```
|
||||
|
||||
2. Import it in your code:
|
||||
|
||||
```go
|
||||
import "github.com/gin-gonic/gin"
|
||||
```
|
||||
|
||||
3. (Optional) Import `net/http`. This is required for example if using constants such as `http.StatusOK`.
|
||||
|
||||
```go
|
||||
import "net/http"
|
||||
```
|
||||
|
||||
##API Examples
|
||||
|
||||
#### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// Creates a gin router with default middleware:
|
||||
// logger and recovery (crash-free) middleware
|
||||
router := gin.Default()
|
||||
|
||||
router.GET("/someGet", getting)
|
||||
router.POST("/somePost", posting)
|
||||
router.PUT("/somePut", putting)
|
||||
router.DELETE("/someDelete", deleting)
|
||||
router.PATCH("/somePatch", patching)
|
||||
router.HEAD("/someHead", head)
|
||||
router.OPTIONS("/someOptions", options)
|
||||
|
||||
// By default it serves on :8080 unless a
|
||||
// PORT environment variable was defined.
|
||||
router.Run()
|
||||
// router.Run(":3000") for a hard coded port
|
||||
}
|
||||
```
|
||||
|
||||
#### Parameters in path
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
// This handler will match /user/john but will not match neither /user/ or /user
|
||||
router.GET("/user/:name", func(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
c.String(http.StatusOK, "Hello %s", name)
|
||||
})
|
||||
|
||||
// However, this one will match /user/john/ and also /user/john/send
|
||||
// If no other routers match /user/john, it will redirect to /user/john/
|
||||
router.GET("/user/:name/*action", func(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
action := c.Param("action")
|
||||
message := name + " is " + action
|
||||
c.String(http.StatusOK, message)
|
||||
})
|
||||
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Querystring parameters
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
// Query string parameters are parsed using the existing underlying request object.
|
||||
// The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe
|
||||
router.GET("/welcome", func(c *gin.Context) {
|
||||
firstname := c.DefaultQuery("firstname", "Guest")
|
||||
lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname")
|
||||
|
||||
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
|
||||
})
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
### Multipart/Urlencoded Form
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
router.POST("/form_post", func(c *gin.Context) {
|
||||
message := c.PostForm("message")
|
||||
nick := c.DefaultPostForm("nick", "anonymous")
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"status": "posted",
|
||||
"message": message,
|
||||
"nick": nick,
|
||||
})
|
||||
})
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
### Another example: query + post form
|
||||
|
||||
```
|
||||
POST /post?id=1234&page=1 HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
name=manu&message=this_is_great
|
||||
```
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
router.POST("/post", func(c *gin.Context) {
|
||||
|
||||
id := c.Query("id")
|
||||
page := c.DefaultQuery("page", "0")
|
||||
name := c.PostForm("name")
|
||||
message := c.PostForm("message")
|
||||
|
||||
fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
|
||||
})
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
id: 1234; page: 1; name: manu; message: this_is_great
|
||||
```
|
||||
|
||||
### Another example: upload file
|
||||
|
||||
References issue [#548](https://github.com/gin-gonic/gin/issues/548).
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
router.POST("/upload", func(c *gin.Context) {
|
||||
|
||||
file, header , err := c.Request.FormFile("upload")
|
||||
filename := header.Filename
|
||||
fmt.Println(header.Filename)
|
||||
out, err := os.Create("./tmp/"+filename+".png")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer out.Close()
|
||||
_, err = io.Copy(out, file)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
})
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Grouping routes
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
// Simple group: v1
|
||||
v1 := router.Group("/v1")
|
||||
{
|
||||
v1.POST("/login", loginEndpoint)
|
||||
v1.POST("/submit", submitEndpoint)
|
||||
v1.POST("/read", readEndpoint)
|
||||
}
|
||||
|
||||
// Simple group: v2
|
||||
v2 := router.Group("/v2")
|
||||
{
|
||||
v2.POST("/login", loginEndpoint)
|
||||
v2.POST("/submit", submitEndpoint)
|
||||
v2.POST("/read", readEndpoint)
|
||||
}
|
||||
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Blank Gin without middleware by default
|
||||
|
||||
Use
|
||||
|
||||
```go
|
||||
r := gin.New()
|
||||
```
|
||||
instead of
|
||||
|
||||
```go
|
||||
r := gin.Default()
|
||||
```
|
||||
|
||||
|
||||
#### Using middleware
|
||||
```go
|
||||
func main() {
|
||||
// Creates a router without any middleware by default
|
||||
r := gin.New()
|
||||
|
||||
// Global middleware
|
||||
r.Use(gin.Logger())
|
||||
r.Use(gin.Recovery())
|
||||
|
||||
// Per route middleware, you can add as many as you desire.
|
||||
r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
|
||||
|
||||
// Authorization group
|
||||
// authorized := r.Group("/", AuthRequired())
|
||||
// exactly the same as:
|
||||
authorized := r.Group("/")
|
||||
// per group middleware! in this case we use the custom created
|
||||
// AuthRequired() middleware just in the "authorized" group.
|
||||
authorized.Use(AuthRequired())
|
||||
{
|
||||
authorized.POST("/login", loginEndpoint)
|
||||
authorized.POST("/submit", submitEndpoint)
|
||||
authorized.POST("/read", readEndpoint)
|
||||
|
||||
// nested group
|
||||
testing := authorized.Group("testing")
|
||||
testing.GET("/analytics", analyticsEndpoint)
|
||||
}
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Model binding and validation
|
||||
|
||||
To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).
|
||||
|
||||
Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`.
|
||||
|
||||
When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use BindWith.
|
||||
|
||||
You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has a empty value when binding, the current request will fail with an error.
|
||||
|
||||
```go
|
||||
// Binding from JSON
|
||||
type Login struct {
|
||||
User string `form:"user" json:"user" binding:"required"`
|
||||
Password string `form:"password" json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
// Example for binding JSON ({"user": "manu", "password": "123"})
|
||||
router.POST("/loginJSON", func(c *gin.Context) {
|
||||
var json Login
|
||||
if c.BindJSON(&json) == nil {
|
||||
if json.User == "manu" && json.Password == "123" {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
|
||||
} else {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Example for binding a HTML form (user=manu&password=123)
|
||||
router.POST("/loginForm", func(c *gin.Context) {
|
||||
var form Login
|
||||
// This will infer what binder to use depending on the content-type header.
|
||||
if c.Bind(&form) == nil {
|
||||
if form.User == "manu" && form.Password == "123" {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
|
||||
} else {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
###Multipart/Urlencoded binding
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
)
|
||||
|
||||
type LoginForm struct {
|
||||
User string `form:"user" binding:"required"`
|
||||
Password string `form:"password" binding:"required"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
router.POST("/login", func(c *gin.Context) {
|
||||
// you can bind multipart form with explicit binding declaration:
|
||||
// c.BindWith(&form, binding.Form)
|
||||
// or you can simply use autobinding with Bind method:
|
||||
var form LoginForm
|
||||
// in this case proper binding will be automatically selected
|
||||
if c.Bind(&form) == nil {
|
||||
if form.User == "user" && form.Password == "password" {
|
||||
c.JSON(200, gin.H{"status": "you are logged in"})
|
||||
} else {
|
||||
c.JSON(401, gin.H{"status": "unauthorized"})
|
||||
}
|
||||
}
|
||||
})
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
Test it with:
|
||||
```sh
|
||||
$ curl -v --form user=user --form password=password http://localhost:8080/login
|
||||
```
|
||||
|
||||
|
||||
#### XML and JSON rendering
|
||||
|
||||
```go
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// gin.H is a shortcut for map[string]interface{}
|
||||
r.GET("/someJSON", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
|
||||
})
|
||||
|
||||
r.GET("/moreJSON", func(c *gin.Context) {
|
||||
// You also can use a struct
|
||||
var msg struct {
|
||||
Name string `json:"user"`
|
||||
Message string
|
||||
Number int
|
||||
}
|
||||
msg.Name = "Lena"
|
||||
msg.Message = "hey"
|
||||
msg.Number = 123
|
||||
// Note that msg.Name becomes "user" in the JSON
|
||||
// Will output : {"user": "Lena", "Message": "hey", "Number": 123}
|
||||
c.JSON(http.StatusOK, msg)
|
||||
})
|
||||
|
||||
r.GET("/someXML", func(c *gin.Context) {
|
||||
c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
####Serving static files
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
router.Static("/assets", "./assets")
|
||||
router.StaticFS("/more_static", http.Dir("my_file_system"))
|
||||
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
####HTML rendering
|
||||
|
||||
Using LoadHTMLTemplates()
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
router.LoadHTMLGlob("templates/*")
|
||||
//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
|
||||
router.GET("/index", func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.tmpl", gin.H{
|
||||
"title": "Main website",
|
||||
})
|
||||
})
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
templates/index.tmpl
|
||||
```html
|
||||
<html>
|
||||
<h1>
|
||||
{{ .title }}
|
||||
</h1>
|
||||
</html>
|
||||
```
|
||||
|
||||
Using templates with same name in different directories
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
router.LoadHTMLGlob("templates/**/*")
|
||||
router.GET("/posts/index", func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
|
||||
"title": "Posts",
|
||||
})
|
||||
})
|
||||
router.GET("/users/index", func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
|
||||
"title": "Users",
|
||||
})
|
||||
})
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
templates/posts/index.tmpl
|
||||
```html
|
||||
{{ define "posts/index.tmpl" }}
|
||||
<html><h1>
|
||||
{{ .title }}
|
||||
</h1>
|
||||
<p>Using posts/index.tmpl</p>
|
||||
</html>
|
||||
{{ end }}
|
||||
```
|
||||
templates/users/index.tmpl
|
||||
```html
|
||||
{{ define "users/index.tmpl" }}
|
||||
<html><h1>
|
||||
{{ .title }}
|
||||
</h1>
|
||||
<p>Using users/index.tmpl</p>
|
||||
</html>
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
You can also use your own html template render
|
||||
|
||||
```go
|
||||
import "html/template"
|
||||
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
html := template.Must(template.ParseFiles("file1", "file2"))
|
||||
router.SetHTMLTemplate(html)
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Redirects
|
||||
|
||||
Issuing a HTTP redirect is easy:
|
||||
|
||||
```go
|
||||
r.GET("/test", func(c *gin.Context) {
|
||||
c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
|
||||
})
|
||||
```
|
||||
Both internal and external locations are supported.
|
||||
|
||||
|
||||
#### Custom Middleware
|
||||
|
||||
```go
|
||||
func Logger() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
t := time.Now()
|
||||
|
||||
// Set example variable
|
||||
c.Set("example", "12345")
|
||||
|
||||
// before request
|
||||
|
||||
c.Next()
|
||||
|
||||
// after request
|
||||
latency := time.Since(t)
|
||||
log.Print(latency)
|
||||
|
||||
// access the status we are sending
|
||||
status := c.Writer.Status()
|
||||
log.Println(status)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gin.New()
|
||||
r.Use(Logger())
|
||||
|
||||
r.GET("/test", func(c *gin.Context) {
|
||||
example := c.MustGet("example").(string)
|
||||
|
||||
// it would print: "12345"
|
||||
log.Println(example)
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Using BasicAuth() middleware
|
||||
```go
|
||||
// simulate some private data
|
||||
var secrets = gin.H{
|
||||
"foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
|
||||
"austin": gin.H{"email": "austin@example.com", "phone": "666"},
|
||||
"lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// Group using gin.BasicAuth() middleware
|
||||
// gin.Accounts is a shortcut for map[string]string
|
||||
authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
|
||||
"foo": "bar",
|
||||
"austin": "1234",
|
||||
"lena": "hello2",
|
||||
"manu": "4321",
|
||||
}))
|
||||
|
||||
// /admin/secrets endpoint
|
||||
// hit "localhost:8080/admin/secrets
|
||||
authorized.GET("/secrets", func(c *gin.Context) {
|
||||
// get user, it was set by the BasicAuth middleware
|
||||
user := c.MustGet(gin.AuthUserKey).(string)
|
||||
if secret, ok := secrets[user]; ok {
|
||||
c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
|
||||
}
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Goroutines inside a middleware
|
||||
When starting inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
r.GET("/long_async", func(c *gin.Context) {
|
||||
// create copy to be used inside the goroutine
|
||||
cCp := c.Copy()
|
||||
go func() {
|
||||
// simulate a long task with time.Sleep(). 5 seconds
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// note that you are using the copied context "cCp", IMPORTANT
|
||||
log.Println("Done! in path " + cCp.Request.URL.Path)
|
||||
}()
|
||||
})
|
||||
|
||||
r.GET("/long_sync", func(c *gin.Context) {
|
||||
// simulate a long task with time.Sleep(). 5 seconds
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// since we are NOT using a goroutine, we do not have to copy the context
|
||||
log.Println("Done! in path " + c.Request.URL.Path)
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
#### Custom HTTP configuration
|
||||
|
||||
Use `http.ListenAndServe()` directly, like this:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
http.ListenAndServe(":8080", router)
|
||||
}
|
||||
```
|
||||
or
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
s := &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: router,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
s.ListenAndServe()
|
||||
}
|
||||
```
|
||||
|
||||
#### Graceful restart or stop
|
||||
|
||||
Do you want to graceful restart or stop your web server?
|
||||
There are some ways this can be done.
|
||||
|
||||
We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details.
|
||||
|
||||
```go
|
||||
router := gin.Default()
|
||||
router.GET("/", handler)
|
||||
// [...]
|
||||
endless.ListenAndServe(":4242", router)
|
||||
```
|
||||
|
||||
An alternative to endless:
|
||||
|
||||
* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully.
|
92
Godeps/_workspace/src/github.com/gin-gonic/gin/auth.go
generated
vendored
92
Godeps/_workspace/src/github.com/gin-gonic/gin/auth.go
generated
vendored
@ -1,92 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const AuthUserKey = "user"
|
||||
|
||||
type (
|
||||
Accounts map[string]string
|
||||
authPair struct {
|
||||
Value string
|
||||
User string
|
||||
}
|
||||
authPairs []authPair
|
||||
)
|
||||
|
||||
func (a authPairs) searchCredential(authValue string) (string, bool) {
|
||||
if len(authValue) == 0 {
|
||||
return "", false
|
||||
}
|
||||
for _, pair := range a {
|
||||
if pair.Value == authValue {
|
||||
return pair.User, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where
|
||||
// the key is the user name and the value is the password, as well as the name of the Realm.
|
||||
// If the realm is empty, "Authorization Required" will be used by default.
|
||||
// (see http://tools.ietf.org/html/rfc2617#section-1.2)
|
||||
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
|
||||
if realm == "" {
|
||||
realm = "Authorization Required"
|
||||
}
|
||||
realm = "Basic realm=" + strconv.Quote(realm)
|
||||
pairs := processAccounts(accounts)
|
||||
return func(c *Context) {
|
||||
// Search user in the slice of allowed credentials
|
||||
user, found := pairs.searchCredential(c.Request.Header.Get("Authorization"))
|
||||
if !found {
|
||||
// Credentials doesn't match, we return 401 and abort handlers chain.
|
||||
c.Header("WWW-Authenticate", realm)
|
||||
c.AbortWithStatus(401)
|
||||
} else {
|
||||
// The user credentials was found, set user's id to key AuthUserKey in this context, the userId can be read later using
|
||||
// c.MustGet(gin.AuthUserKey)
|
||||
c.Set(AuthUserKey, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where
|
||||
// the key is the user name and the value is the password.
|
||||
func BasicAuth(accounts Accounts) HandlerFunc {
|
||||
return BasicAuthForRealm(accounts, "")
|
||||
}
|
||||
|
||||
func processAccounts(accounts Accounts) authPairs {
|
||||
assert1(len(accounts) > 0, "Empty list of authorized credentials")
|
||||
pairs := make(authPairs, 0, len(accounts))
|
||||
for user, password := range accounts {
|
||||
assert1(len(user) > 0, "User can not be empty")
|
||||
value := authorizationHeader(user, password)
|
||||
pairs = append(pairs, authPair{
|
||||
Value: value,
|
||||
User: user,
|
||||
})
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
|
||||
func authorizationHeader(user, password string) string {
|
||||
base := user + ":" + password
|
||||
return "Basic " + base64.StdEncoding.EncodeToString([]byte(base))
|
||||
}
|
||||
|
||||
func secureCompare(given, actual string) bool {
|
||||
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
||||
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
||||
}
|
||||
/* Securely compare actual to itself to keep constant time, but always return false */
|
||||
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
||||
}
|
67
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
67
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
@ -1,67 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package binding
|
||||
|
||||
import "net/http"
|
||||
|
||||
const (
|
||||
MIMEJSON = "application/json"
|
||||
MIMEHTML = "text/html"
|
||||
MIMEXML = "application/xml"
|
||||
MIMEXML2 = "text/xml"
|
||||
MIMEPlain = "text/plain"
|
||||
MIMEPOSTForm = "application/x-www-form-urlencoded"
|
||||
MIMEMultipartPOSTForm = "multipart/form-data"
|
||||
MIMEPROTOBUF = "application/x-protobuf"
|
||||
)
|
||||
|
||||
type Binding interface {
|
||||
Name() string
|
||||
Bind(*http.Request, interface{}) error
|
||||
}
|
||||
|
||||
type StructValidator interface {
|
||||
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
||||
// If the received type is not a struct, any validation should be skipped and nil must be returned.
|
||||
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
||||
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
||||
// Otherwise nil must be returned.
|
||||
ValidateStruct(interface{}) error
|
||||
}
|
||||
|
||||
var Validator StructValidator = &defaultValidator{}
|
||||
|
||||
var (
|
||||
JSON = jsonBinding{}
|
||||
XML = xmlBinding{}
|
||||
Form = formBinding{}
|
||||
FormPost = formPostBinding{}
|
||||
FormMultipart = formMultipartBinding{}
|
||||
ProtoBuf = protobufBinding{}
|
||||
)
|
||||
|
||||
func Default(method, contentType string) Binding {
|
||||
if method == "GET" {
|
||||
return Form
|
||||
} else {
|
||||
switch contentType {
|
||||
case MIMEJSON:
|
||||
return JSON
|
||||
case MIMEXML, MIMEXML2:
|
||||
return XML
|
||||
case MIMEPROTOBUF:
|
||||
return ProtoBuf
|
||||
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
|
||||
return Form
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validate(obj interface{}) error {
|
||||
if Validator == nil {
|
||||
return nil
|
||||
}
|
||||
return Validator.ValidateStruct(obj)
|
||||
}
|
41
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/default_validator.go
generated
vendored
41
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/default_validator.go
generated
vendored
@ -1,41 +0,0 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/go-playground/validator.v8"
|
||||
)
|
||||
|
||||
type defaultValidator struct {
|
||||
once sync.Once
|
||||
validate *validator.Validate
|
||||
}
|
||||
|
||||
var _ StructValidator = &defaultValidator{}
|
||||
|
||||
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
||||
if kindOfData(obj) == reflect.Struct {
|
||||
v.lazyinit()
|
||||
if err := v.validate.Struct(obj); err != nil {
|
||||
return error(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *defaultValidator) lazyinit() {
|
||||
v.once.Do(func() {
|
||||
config := &validator.Config{TagName: "binding"}
|
||||
v.validate = validator.New(config)
|
||||
})
|
||||
}
|
||||
|
||||
func kindOfData(data interface{}) reflect.Kind {
|
||||
value := reflect.ValueOf(data)
|
||||
valueType := value.Kind()
|
||||
if valueType == reflect.Ptr {
|
||||
valueType = value.Elem().Kind()
|
||||
}
|
||||
return valueType
|
||||
}
|
54
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/form.go
generated
vendored
54
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/form.go
generated
vendored
@ -1,54 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package binding
|
||||
|
||||
import "net/http"
|
||||
|
||||
type formBinding struct{}
|
||||
type formPostBinding struct{}
|
||||
type formMultipartBinding struct{}
|
||||
|
||||
func (formBinding) Name() string {
|
||||
return "form"
|
||||
}
|
||||
|
||||
func (formBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
req.ParseMultipartForm(32 << 10) // 32 MB
|
||||
if err := mapForm(obj, req.Form); err != nil {
|
||||
return err
|
||||
}
|
||||
return validate(obj)
|
||||
}
|
||||
|
||||
func (formPostBinding) Name() string {
|
||||
return "form-urlencoded"
|
||||
}
|
||||
|
||||
func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mapForm(obj, req.PostForm); err != nil {
|
||||
return err
|
||||
}
|
||||
return validate(obj)
|
||||
}
|
||||
|
||||
func (formMultipartBinding) Name() string {
|
||||
return "multipart/form-data"
|
||||
}
|
||||
|
||||
func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
if err := req.ParseMultipartForm(32 << 10); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mapForm(obj, req.MultipartForm.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
return validate(obj)
|
||||
}
|
150
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/form_mapping.go
generated
vendored
150
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/form_mapping.go
generated
vendored
@ -1,150 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func mapForm(ptr interface{}, form map[string][]string) error {
|
||||
typ := reflect.TypeOf(ptr).Elem()
|
||||
val := reflect.ValueOf(ptr).Elem()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
typeField := typ.Field(i)
|
||||
structField := val.Field(i)
|
||||
if !structField.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
structFieldKind := structField.Kind()
|
||||
inputFieldName := typeField.Tag.Get("form")
|
||||
if inputFieldName == "" {
|
||||
inputFieldName = typeField.Name
|
||||
|
||||
// if "form" tag is nil, we inspect if the field is a struct.
|
||||
// this would not make sense for JSON parsing but it does for a form
|
||||
// since data is flatten
|
||||
if structFieldKind == reflect.Struct {
|
||||
err := mapForm(structField.Addr().Interface(), form)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
inputValue, exists := form[inputFieldName]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
numElems := len(inputValue)
|
||||
if structFieldKind == reflect.Slice && numElems > 0 {
|
||||
sliceOf := structField.Type().Elem().Kind()
|
||||
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
||||
for i := 0; i < numElems; i++ {
|
||||
if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
val.Field(i).Set(slice)
|
||||
} else {
|
||||
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
|
||||
switch valueKind {
|
||||
case reflect.Int:
|
||||
return setIntField(val, 0, structField)
|
||||
case reflect.Int8:
|
||||
return setIntField(val, 8, structField)
|
||||
case reflect.Int16:
|
||||
return setIntField(val, 16, structField)
|
||||
case reflect.Int32:
|
||||
return setIntField(val, 32, structField)
|
||||
case reflect.Int64:
|
||||
return setIntField(val, 64, structField)
|
||||
case reflect.Uint:
|
||||
return setUintField(val, 0, structField)
|
||||
case reflect.Uint8:
|
||||
return setUintField(val, 8, structField)
|
||||
case reflect.Uint16:
|
||||
return setUintField(val, 16, structField)
|
||||
case reflect.Uint32:
|
||||
return setUintField(val, 32, structField)
|
||||
case reflect.Uint64:
|
||||
return setUintField(val, 64, structField)
|
||||
case reflect.Bool:
|
||||
return setBoolField(val, structField)
|
||||
case reflect.Float32:
|
||||
return setFloatField(val, 32, structField)
|
||||
case reflect.Float64:
|
||||
return setFloatField(val, 64, structField)
|
||||
case reflect.String:
|
||||
structField.SetString(val)
|
||||
default:
|
||||
return errors.New("Unknown type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setIntField(val string, bitSize int, field reflect.Value) error {
|
||||
if val == "" {
|
||||
val = "0"
|
||||
}
|
||||
intVal, err := strconv.ParseInt(val, 10, bitSize)
|
||||
if err == nil {
|
||||
field.SetInt(intVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setUintField(val string, bitSize int, field reflect.Value) error {
|
||||
if val == "" {
|
||||
val = "0"
|
||||
}
|
||||
uintVal, err := strconv.ParseUint(val, 10, bitSize)
|
||||
if err == nil {
|
||||
field.SetUint(uintVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setBoolField(val string, field reflect.Value) error {
|
||||
if val == "" {
|
||||
val = "false"
|
||||
}
|
||||
boolVal, err := strconv.ParseBool(val)
|
||||
if err == nil {
|
||||
field.SetBool(boolVal)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFloatField(val string, bitSize int, field reflect.Value) error {
|
||||
if val == "" {
|
||||
val = "0.0"
|
||||
}
|
||||
floatVal, err := strconv.ParseFloat(val, bitSize)
|
||||
if err == nil {
|
||||
field.SetFloat(floatVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Don't pass in pointers to bind to. Can lead to bugs. See:
|
||||
// https://github.com/codegangsta/martini-contrib/issues/40
|
||||
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
|
||||
func ensureNotPointer(obj interface{}) {
|
||||
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
|
||||
panic("Pointers are not accepted as binding models")
|
||||
}
|
||||
}
|
25
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/json.go
generated
vendored
25
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/json.go
generated
vendored
@ -1,25 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type jsonBinding struct{}
|
||||
|
||||
func (jsonBinding) Name() string {
|
||||
return "json"
|
||||
}
|
||||
|
||||
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
if err := decoder.Decode(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
return validate(obj)
|
||||
}
|
35
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/protobuf.go
generated
vendored
35
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/protobuf.go
generated
vendored
@ -1,35 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type protobufBinding struct{}
|
||||
|
||||
func (protobufBinding) Name() string {
|
||||
return "protobuf"
|
||||
}
|
||||
|
||||
func (protobufBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
|
||||
buf, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = proto.Unmarshal(buf, obj.(proto.Message)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//Here it's same to return validate(obj), but util now we cann't add `binding:""` to the struct
|
||||
//which automatically generate by gen-proto
|
||||
return nil
|
||||
//return validate(obj)
|
||||
}
|
24
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/xml.go
generated
vendored
24
Godeps/_workspace/src/github.com/gin-gonic/gin/binding/xml.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type xmlBinding struct{}
|
||||
|
||||
func (xmlBinding) Name() string {
|
||||
return "xml"
|
||||
}
|
||||
|
||||
func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
decoder := xml.NewDecoder(req.Body)
|
||||
if err := decoder.Decode(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
return validate(obj)
|
||||
}
|
568
Godeps/_workspace/src/github.com/gin-gonic/gin/context.go
generated
vendored
568
Godeps/_workspace/src/github.com/gin-gonic/gin/context.go
generated
vendored
@ -1,568 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/gin-gonic/gin/render"
|
||||
"github.com/manucorporat/sse"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Content-Type MIME of the most common data formats
|
||||
const (
|
||||
MIMEJSON = binding.MIMEJSON
|
||||
MIMEHTML = binding.MIMEHTML
|
||||
MIMEXML = binding.MIMEXML
|
||||
MIMEXML2 = binding.MIMEXML2
|
||||
MIMEPlain = binding.MIMEPlain
|
||||
MIMEPOSTForm = binding.MIMEPOSTForm
|
||||
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
|
||||
)
|
||||
|
||||
const abortIndex int8 = math.MaxInt8 / 2
|
||||
|
||||
// Context is the most important part of gin. It allows us to pass variables between middleware,
|
||||
// manage the flow, validate the JSON of a request and render a JSON response for example.
|
||||
type Context struct {
|
||||
writermem responseWriter
|
||||
Request *http.Request
|
||||
Writer ResponseWriter
|
||||
|
||||
Params Params
|
||||
handlers HandlersChain
|
||||
index int8
|
||||
|
||||
engine *Engine
|
||||
Keys map[string]interface{}
|
||||
Errors errorMsgs
|
||||
Accepted []string
|
||||
}
|
||||
|
||||
var _ context.Context = &Context{}
|
||||
|
||||
/************************************/
|
||||
/********** CONTEXT CREATION ********/
|
||||
/************************************/
|
||||
|
||||
func (c *Context) reset() {
|
||||
c.Writer = &c.writermem
|
||||
c.Params = c.Params[0:0]
|
||||
c.handlers = nil
|
||||
c.index = -1
|
||||
c.Keys = nil
|
||||
c.Errors = c.Errors[0:0]
|
||||
c.Accepted = nil
|
||||
}
|
||||
|
||||
// Copy returns a copy of the current context that can be safely used outside the request's scope.
|
||||
// This have to be used then the context has to be passed to a goroutine.
|
||||
func (c *Context) Copy() *Context {
|
||||
var cp = *c
|
||||
cp.writermem.ResponseWriter = nil
|
||||
cp.Writer = &cp.writermem
|
||||
cp.index = abortIndex
|
||||
cp.handlers = nil
|
||||
return &cp
|
||||
}
|
||||
|
||||
// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", this
|
||||
// function will return "main.handleGetUsers"
|
||||
func (c *Context) HandlerName() string {
|
||||
return nameOfFunction(c.handlers.Last())
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/*********** FLOW CONTROL ***********/
|
||||
/************************************/
|
||||
|
||||
// Next should be used only inside middleware.
|
||||
// It executes the pending handlers in the chain inside the calling handler.
|
||||
// See example in github.
|
||||
func (c *Context) Next() {
|
||||
c.index++
|
||||
s := int8(len(c.handlers))
|
||||
for ; c.index < s; c.index++ {
|
||||
c.handlers[c.index](c)
|
||||
}
|
||||
}
|
||||
|
||||
// IsAborted returns true if the current context was aborted.
|
||||
func (c *Context) IsAborted() bool {
|
||||
return c.index >= abortIndex
|
||||
}
|
||||
|
||||
// Abort prevents pending handlers from being called. Note that this will not stop the current handler.
|
||||
// Let's say you have an authorization middleware that validates that the current request is authorized. If the
|
||||
// authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
|
||||
// for this request are not called.
|
||||
func (c *Context) Abort() {
|
||||
c.index = abortIndex
|
||||
}
|
||||
|
||||
// AbortWithStatus calls `Abort()` and writes the headers with the specified status code.
|
||||
// For example, a failed attempt to authentificate a request could use: context.AbortWithStatus(401).
|
||||
func (c *Context) AbortWithStatus(code int) {
|
||||
c.Status(code)
|
||||
c.Writer.WriteHeaderNow()
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
// AbortWithError calls `AbortWithStatus()` and `Error()` internally. This method stops the chain, writes the status code and
|
||||
// pushes the specified error to `c.Errors`.
|
||||
// See Context.Error() for more details.
|
||||
func (c *Context) AbortWithError(code int, err error) *Error {
|
||||
c.AbortWithStatus(code)
|
||||
return c.Error(err)
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/********* ERROR MANAGEMENT *********/
|
||||
/************************************/
|
||||
|
||||
// Attaches an error to the current context. The error is pushed to a list of errors.
|
||||
// It's a good idea to call Error for each error that occurred during the resolution of a request.
|
||||
// A middleware can be used to collect all the errors
|
||||
// and push them to a database together, print a log, or append it in the HTTP response.
|
||||
func (c *Context) Error(err error) *Error {
|
||||
var parsedError *Error
|
||||
switch err.(type) {
|
||||
case *Error:
|
||||
parsedError = err.(*Error)
|
||||
default:
|
||||
parsedError = &Error{
|
||||
Err: err,
|
||||
Type: ErrorTypePrivate,
|
||||
}
|
||||
}
|
||||
c.Errors = append(c.Errors, parsedError)
|
||||
return parsedError
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/******** METADATA MANAGEMENT********/
|
||||
/************************************/
|
||||
|
||||
// Set is used to store a new key/value pair exclusivelly for this context.
|
||||
// It also lazy initializes c.Keys if it was not used previously.
|
||||
func (c *Context) Set(key string, value interface{}) {
|
||||
if c.Keys == nil {
|
||||
c.Keys = make(map[string]interface{})
|
||||
}
|
||||
c.Keys[key] = value
|
||||
}
|
||||
|
||||
// Get returns the value for the given key, ie: (value, true).
|
||||
// If the value does not exists it returns (nil, false)
|
||||
func (c *Context) Get(key string) (value interface{}, exists bool) {
|
||||
if c.Keys != nil {
|
||||
value, exists = c.Keys[key]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MustGet returns the value for the given key if it exists, otherwise it panics.
|
||||
func (c *Context) MustGet(key string) interface{} {
|
||||
if value, exists := c.Get(key); exists {
|
||||
return value
|
||||
}
|
||||
panic("Key \"" + key + "\" does not exist")
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/************ INPUT DATA ************/
|
||||
/************************************/
|
||||
|
||||
// Param returns the value of the URL param.
|
||||
// It is a shortcut for c.Params.ByName(key)
|
||||
// router.GET("/user/:id", func(c *gin.Context) {
|
||||
// // a GET request to /user/john
|
||||
// id := c.Param("id") // id == "john"
|
||||
// })
|
||||
func (c *Context) Param(key string) string {
|
||||
return c.Params.ByName(key)
|
||||
}
|
||||
|
||||
// Query returns the keyed url query value if it exists,
|
||||
// othewise it returns an empty string `("")`.
|
||||
// It is shortcut for `c.Request.URL.Query().Get(key)`
|
||||
// GET /path?id=1234&name=Manu&value=
|
||||
// c.Query("id") == "1234"
|
||||
// c.Query("name") == "Manu"
|
||||
// c.Query("value") == ""
|
||||
// c.Query("wtf") == ""
|
||||
func (c *Context) Query(key string) string {
|
||||
value, _ := c.GetQuery(key)
|
||||
return value
|
||||
}
|
||||
|
||||
// DefaultQuery returns the keyed url query value if it exists,
|
||||
// othewise it returns the specified defaultValue string.
|
||||
// See: Query() and GetQuery() for further information.
|
||||
// GET /?name=Manu&lastname=
|
||||
// c.DefaultQuery("name", "unknown") == "Manu"
|
||||
// c.DefaultQuery("id", "none") == "none"
|
||||
// c.DefaultQuery("lastname", "none") == ""
|
||||
func (c *Context) DefaultQuery(key, defaultValue string) string {
|
||||
if value, ok := c.GetQuery(key); ok {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// GetQuery is like Query(), it returns the keyed url query value
|
||||
// if it exists `(value, true)` (even when the value is an empty string),
|
||||
// othewise it returns `("", false)`.
|
||||
// It is shortcut for `c.Request.URL.Query().Get(key)`
|
||||
// GET /?name=Manu&lastname=
|
||||
// ("Manu", true) == c.GetQuery("name")
|
||||
// ("", false) == c.GetQuery("id")
|
||||
// ("", true) == c.GetQuery("lastname")
|
||||
func (c *Context) GetQuery(key string) (string, bool) {
|
||||
req := c.Request
|
||||
if values, ok := req.URL.Query()[key]; ok && len(values) > 0 {
|
||||
return values[0], true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// PostForm returns the specified key from a POST urlencoded form or multipart form
|
||||
// when it exists, otherwise it returns an empty string `("")`.
|
||||
func (c *Context) PostForm(key string) string {
|
||||
value, _ := c.GetPostForm(key)
|
||||
return value
|
||||
}
|
||||
|
||||
// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form
|
||||
// when it exists, otherwise it returns the specified defaultValue string.
|
||||
// See: PostForm() and GetPostForm() for further information.
|
||||
func (c *Context) DefaultPostForm(key, defaultValue string) string {
|
||||
if value, ok := c.GetPostForm(key); ok {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded
|
||||
// form or multipart form when it exists `(value, true)` (even when the value is an empty string),
|
||||
// otherwise it returns ("", false).
|
||||
// For example, during a PATCH request to update the user's email:
|
||||
// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
|
||||
// email= --> ("", true) := GetPostForm("email") // set email to ""
|
||||
// --> ("", false) := GetPostForm("email") // do nothing with email
|
||||
func (c *Context) GetPostForm(key string) (string, bool) {
|
||||
req := c.Request
|
||||
req.ParseMultipartForm(32 << 20) // 32 MB
|
||||
if values := req.PostForm[key]; len(values) > 0 {
|
||||
return values[0], true
|
||||
}
|
||||
if req.MultipartForm != nil && req.MultipartForm.File != nil {
|
||||
if values := req.MultipartForm.Value[key]; len(values) > 0 {
|
||||
return values[0], true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Bind checks the Content-Type to select a binding engine automatically,
|
||||
// Depending the "Content-Type" header different bindings are used:
|
||||
// "application/json" --> JSON binding
|
||||
// "application/xml" --> XML binding
|
||||
// otherwise --> returns an error
|
||||
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
|
||||
// It decodes the json payload into the struct specified as a pointer.
|
||||
// Like ParseBody() but this method also writes a 400 error if the json is not valid.
|
||||
func (c *Context) Bind(obj interface{}) error {
|
||||
b := binding.Default(c.Request.Method, c.ContentType())
|
||||
return c.BindWith(obj, b)
|
||||
}
|
||||
|
||||
// BindJSON is a shortcut for c.BindWith(obj, binding.JSON)
|
||||
func (c *Context) BindJSON(obj interface{}) error {
|
||||
return c.BindWith(obj, binding.JSON)
|
||||
}
|
||||
|
||||
// BindWith binds the passed struct pointer using the specified binding engine.
|
||||
// See the binding package.
|
||||
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
||||
if err := b.Bind(c.Request, obj); err != nil {
|
||||
c.AbortWithError(400, err).SetType(ErrorTypeBind)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClientIP implements a best effort algorithm to return the real client IP, it parses
|
||||
// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
|
||||
func (c *Context) ClientIP() string {
|
||||
if c.engine.ForwardedByClientIP {
|
||||
clientIP := strings.TrimSpace(c.requestHeader("X-Real-Ip"))
|
||||
if len(clientIP) > 0 {
|
||||
return clientIP
|
||||
}
|
||||
clientIP = c.requestHeader("X-Forwarded-For")
|
||||
if index := strings.IndexByte(clientIP, ','); index >= 0 {
|
||||
clientIP = clientIP[0:index]
|
||||
}
|
||||
clientIP = strings.TrimSpace(clientIP)
|
||||
if len(clientIP) > 0 {
|
||||
return clientIP
|
||||
}
|
||||
}
|
||||
if ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)); err == nil {
|
||||
return ip
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ContentType returns the Content-Type header of the request.
|
||||
func (c *Context) ContentType() string {
|
||||
return filterFlags(c.requestHeader("Content-Type"))
|
||||
}
|
||||
|
||||
func (c *Context) requestHeader(key string) string {
|
||||
if values, _ := c.Request.Header[key]; len(values) > 0 {
|
||||
return values[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/******** RESPONSE RENDERING ********/
|
||||
/************************************/
|
||||
|
||||
func (c *Context) Status(code int) {
|
||||
c.writermem.WriteHeader(code)
|
||||
}
|
||||
|
||||
// Header is a intelligent shortcut for c.Writer.Header().Set(key, value)
|
||||
// It writes a header in the response.
|
||||
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
|
||||
func (c *Context) Header(key, value string) {
|
||||
if len(value) == 0 {
|
||||
c.Writer.Header().Del(key)
|
||||
} else {
|
||||
c.Writer.Header().Set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) SetCookie(
|
||||
name string,
|
||||
value string,
|
||||
maxAge int,
|
||||
path string,
|
||||
domain string,
|
||||
secure bool,
|
||||
httpOnly bool,
|
||||
) {
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
http.SetCookie(c.Writer, &http.Cookie{
|
||||
Name: name,
|
||||
Value: url.QueryEscape(value),
|
||||
MaxAge: maxAge,
|
||||
Path: path,
|
||||
Domain: domain,
|
||||
Secure: secure,
|
||||
HttpOnly: httpOnly,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Context) Cookie(name string) (string, error) {
|
||||
cookie, err := c.Request.Cookie(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
val, _ := url.QueryUnescape(cookie.Value)
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (c *Context) Render(code int, r render.Render) {
|
||||
c.Status(code)
|
||||
if err := r.Render(c.Writer); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// HTML renders the HTTP template specified by its file name.
|
||||
// It also updates the HTTP code and sets the Content-Type as "text/html".
|
||||
// See http://golang.org/doc/articles/wiki/
|
||||
func (c *Context) HTML(code int, name string, obj interface{}) {
|
||||
instance := c.engine.HTMLRender.Instance(name, obj)
|
||||
c.Render(code, instance)
|
||||
}
|
||||
|
||||
// IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body.
|
||||
// It also sets the Content-Type as "application/json".
|
||||
// WARNING: we recommend to use this only for development propuses since printing pretty JSON is
|
||||
// more CPU and bandwidth consuming. Use Context.JSON() instead.
|
||||
func (c *Context) IndentedJSON(code int, obj interface{}) {
|
||||
c.Render(code, render.IndentedJSON{Data: obj})
|
||||
}
|
||||
|
||||
// JSON serializes the given struct as JSON into the response body.
|
||||
// It also sets the Content-Type as "application/json".
|
||||
func (c *Context) JSON(code int, obj interface{}) {
|
||||
c.Status(code)
|
||||
if err := render.WriteJSON(c.Writer, obj); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// XML serializes the given struct as XML into the response body.
|
||||
// It also sets the Content-Type as "application/xml".
|
||||
func (c *Context) XML(code int, obj interface{}) {
|
||||
c.Render(code, render.XML{Data: obj})
|
||||
}
|
||||
|
||||
// YAML serializes the given struct as YAML into the response body.
|
||||
func (c *Context) YAML(code int, obj interface{}) {
|
||||
c.Render(code, render.YAML{Data: obj})
|
||||
}
|
||||
|
||||
// String writes the given string into the response body.
|
||||
func (c *Context) String(code int, format string, values ...interface{}) {
|
||||
c.Status(code)
|
||||
render.WriteString(c.Writer, format, values)
|
||||
}
|
||||
|
||||
// Redirect returns a HTTP redirect to the specific location.
|
||||
func (c *Context) Redirect(code int, location string) {
|
||||
c.Render(-1, render.Redirect{
|
||||
Code: code,
|
||||
Location: location,
|
||||
Request: c.Request,
|
||||
})
|
||||
}
|
||||
|
||||
// Data writes some data into the body stream and updates the HTTP code.
|
||||
func (c *Context) Data(code int, contentType string, data []byte) {
|
||||
c.Render(code, render.Data{
|
||||
ContentType: contentType,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// File writes the specified file into the body stream in a efficient way.
|
||||
func (c *Context) File(filepath string) {
|
||||
http.ServeFile(c.Writer, c.Request, filepath)
|
||||
}
|
||||
|
||||
// SSEvent writes a Server-Sent Event into the body stream.
|
||||
func (c *Context) SSEvent(name string, message interface{}) {
|
||||
c.Render(-1, sse.Event{
|
||||
Event: name,
|
||||
Data: message,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Context) Stream(step func(w io.Writer) bool) {
|
||||
w := c.Writer
|
||||
clientGone := w.CloseNotify()
|
||||
for {
|
||||
select {
|
||||
case <-clientGone:
|
||||
return
|
||||
default:
|
||||
keepOpen := step(w)
|
||||
w.Flush()
|
||||
if !keepOpen {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/******** CONTENT NEGOTIATION *******/
|
||||
/************************************/
|
||||
|
||||
type Negotiate struct {
|
||||
Offered []string
|
||||
HTMLName string
|
||||
HTMLData interface{}
|
||||
JSONData interface{}
|
||||
XMLData interface{}
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func (c *Context) Negotiate(code int, config Negotiate) {
|
||||
switch c.NegotiateFormat(config.Offered...) {
|
||||
case binding.MIMEJSON:
|
||||
data := chooseData(config.JSONData, config.Data)
|
||||
c.JSON(code, data)
|
||||
|
||||
case binding.MIMEHTML:
|
||||
data := chooseData(config.HTMLData, config.Data)
|
||||
c.HTML(code, config.HTMLName, data)
|
||||
|
||||
case binding.MIMEXML:
|
||||
data := chooseData(config.XMLData, config.Data)
|
||||
c.XML(code, data)
|
||||
|
||||
default:
|
||||
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server"))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) NegotiateFormat(offered ...string) string {
|
||||
assert1(len(offered) > 0, "you must provide at least one offer")
|
||||
|
||||
if c.Accepted == nil {
|
||||
c.Accepted = parseAccept(c.requestHeader("Accept"))
|
||||
}
|
||||
if len(c.Accepted) == 0 {
|
||||
return offered[0]
|
||||
}
|
||||
for _, accepted := range c.Accepted {
|
||||
for _, offert := range offered {
|
||||
if accepted == offert {
|
||||
return offert
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *Context) SetAccepted(formats ...string) {
|
||||
c.Accepted = formats
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/***** GOLANG.ORG/X/NET/CONTEXT *****/
|
||||
/************************************/
|
||||
|
||||
func (c *Context) Deadline() (deadline time.Time, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Context) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Context) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Context) Value(key interface{}) interface{} {
|
||||
if key == 0 {
|
||||
return c.Request
|
||||
}
|
||||
if keyAsString, ok := key.(string); ok {
|
||||
val, _ := c.Get(keyAsString)
|
||||
return val
|
||||
}
|
||||
return nil
|
||||
}
|
71
Godeps/_workspace/src/github.com/gin-gonic/gin/debug.go
generated
vendored
71
Godeps/_workspace/src/github.com/gin-gonic/gin/debug.go
generated
vendored
@ -1,71 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"log"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(0)
|
||||
}
|
||||
|
||||
// IsDebugging returns true if the framework is running in debug mode.
|
||||
// Use SetMode(gin.Release) to switch to disable the debug mode.
|
||||
func IsDebugging() bool {
|
||||
return ginMode == debugCode
|
||||
}
|
||||
|
||||
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
|
||||
if IsDebugging() {
|
||||
nuHandlers := len(handlers)
|
||||
handlerName := nameOfFunction(handlers.Last())
|
||||
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
|
||||
}
|
||||
}
|
||||
|
||||
func debugPrintLoadTemplate(tmpl *template.Template) {
|
||||
if IsDebugging() {
|
||||
var buf bytes.Buffer
|
||||
for _, tmpl := range tmpl.Templates() {
|
||||
buf.WriteString("\t- ")
|
||||
buf.WriteString(tmpl.Name())
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
debugPrint("Loaded HTML Templates (%d): \n%s\n", len(tmpl.Templates()), buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func debugPrint(format string, values ...interface{}) {
|
||||
if IsDebugging() {
|
||||
log.Printf("[GIN-debug] "+format, values...)
|
||||
}
|
||||
}
|
||||
|
||||
func debugPrintWARNINGNew() {
|
||||
debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production.
|
||||
- using env: export GIN_MODE=release
|
||||
- using code: gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
func debugPrintWARNINGSetHTMLTemplate() {
|
||||
debugPrint(`[WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called
|
||||
at initialization. ie. before any route is registered or the router is listening in a socket:
|
||||
|
||||
router := gin.Default()
|
||||
router.SetHTMLTemplate(template) // << good place
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
func debugPrintError(err error) {
|
||||
if err != nil {
|
||||
debugPrint("[ERROR] %v\n", err)
|
||||
}
|
||||
}
|
12
Godeps/_workspace/src/github.com/gin-gonic/gin/deprecated.go
generated
vendored
12
Godeps/_workspace/src/github.com/gin-gonic/gin/deprecated.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import "log"
|
||||
|
||||
func (c *Context) GetCookie(name string) (string, error) {
|
||||
log.Println("GetCookie() method is deprecated. Use Cookie() instead.")
|
||||
return c.Cookie(name)
|
||||
}
|
159
Godeps/_workspace/src/github.com/gin-gonic/gin/errors.go
generated
vendored
159
Godeps/_workspace/src/github.com/gin-gonic/gin/errors.go
generated
vendored
@ -1,159 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type ErrorType uint64
|
||||
|
||||
const (
|
||||
ErrorTypeBind ErrorType = 1 << 63 // used when c.Bind() fails
|
||||
ErrorTypeRender ErrorType = 1 << 62 // used when c.Render() fails
|
||||
ErrorTypePrivate ErrorType = 1 << 0
|
||||
ErrorTypePublic ErrorType = 1 << 1
|
||||
|
||||
ErrorTypeAny ErrorType = 1<<64 - 1
|
||||
ErrorTypeNu = 2
|
||||
)
|
||||
|
||||
type (
|
||||
Error struct {
|
||||
Err error
|
||||
Type ErrorType
|
||||
Meta interface{}
|
||||
}
|
||||
|
||||
errorMsgs []*Error
|
||||
)
|
||||
|
||||
var _ error = &Error{}
|
||||
|
||||
func (msg *Error) SetType(flags ErrorType) *Error {
|
||||
msg.Type = flags
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *Error) SetMeta(data interface{}) *Error {
|
||||
msg.Meta = data
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *Error) JSON() interface{} {
|
||||
json := H{}
|
||||
if msg.Meta != nil {
|
||||
value := reflect.ValueOf(msg.Meta)
|
||||
switch value.Kind() {
|
||||
case reflect.Struct:
|
||||
return msg.Meta
|
||||
case reflect.Map:
|
||||
for _, key := range value.MapKeys() {
|
||||
json[key.String()] = value.MapIndex(key).Interface()
|
||||
}
|
||||
default:
|
||||
json["meta"] = msg.Meta
|
||||
}
|
||||
}
|
||||
if _, ok := json["error"]; !ok {
|
||||
json["error"] = msg.Error()
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaller interface
|
||||
func (msg *Error) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(msg.JSON())
|
||||
}
|
||||
|
||||
// Implements the error interface
|
||||
func (msg *Error) Error() string {
|
||||
return msg.Err.Error()
|
||||
}
|
||||
|
||||
func (msg *Error) IsType(flags ErrorType) bool {
|
||||
return (msg.Type & flags) > 0
|
||||
}
|
||||
|
||||
// Returns a readonly copy filterd the byte.
|
||||
// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic
|
||||
func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
if typ == ErrorTypeAny {
|
||||
return a
|
||||
}
|
||||
var result errorMsgs
|
||||
for _, msg := range a {
|
||||
if msg.IsType(typ) {
|
||||
result = append(result, msg)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the last error in the slice. It returns nil if the array is empty.
|
||||
// Shortcut for errors[len(errors)-1]
|
||||
func (a errorMsgs) Last() *Error {
|
||||
length := len(a)
|
||||
if length > 0 {
|
||||
return a[length-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns an array will all the error messages.
|
||||
// Example:
|
||||
// c.Error(errors.New("first"))
|
||||
// c.Error(errors.New("second"))
|
||||
// c.Error(errors.New("third"))
|
||||
// c.Errors.Errors() // == []string{"first", "second", "third"}
|
||||
func (a errorMsgs) Errors() []string {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
errorStrings := make([]string, len(a))
|
||||
for i, err := range a {
|
||||
errorStrings[i] = err.Error()
|
||||
}
|
||||
return errorStrings
|
||||
}
|
||||
|
||||
func (a errorMsgs) JSON() interface{} {
|
||||
switch len(a) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return a.Last().JSON()
|
||||
default:
|
||||
json := make([]interface{}, len(a))
|
||||
for i, err := range a {
|
||||
json[i] = err.JSON()
|
||||
}
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
func (a errorMsgs) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(a.JSON())
|
||||
}
|
||||
|
||||
func (a errorMsgs) String() string {
|
||||
if len(a) == 0 {
|
||||
return ""
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
for i, msg := range a {
|
||||
fmt.Fprintf(&buffer, "Error #%02d: %s\n", (i + 1), msg.Err)
|
||||
if msg.Meta != nil {
|
||||
fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
42
Godeps/_workspace/src/github.com/gin-gonic/gin/fs.go
generated
vendored
42
Godeps/_workspace/src/github.com/gin-gonic/gin/fs.go
generated
vendored
@ -1,42 +0,0 @@
|
||||
package gin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
type (
|
||||
onlyfilesFS struct {
|
||||
fs http.FileSystem
|
||||
}
|
||||
neuteredReaddirFile struct {
|
||||
http.File
|
||||
}
|
||||
)
|
||||
|
||||
// Dir returns a http.Filesystem that can be used by http.FileServer(). It is used interally
|
||||
// in router.Static().
|
||||
// if listDirectory == true, then it works the same as http.Dir() otherwise it returns
|
||||
// a filesystem that prevents http.FileServer() to list the directory files.
|
||||
func Dir(root string, listDirectory bool) http.FileSystem {
|
||||
fs := http.Dir(root)
|
||||
if listDirectory {
|
||||
return fs
|
||||
}
|
||||
return &onlyfilesFS{fs}
|
||||
}
|
||||
|
||||
// Conforms to http.Filesystem
|
||||
func (fs onlyfilesFS) Open(name string) (http.File, error) {
|
||||
f, err := fs.fs.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return neuteredReaddirFile{f}, nil
|
||||
}
|
||||
|
||||
// Overrides the http.File default implementation
|
||||
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
// this disables directory listing
|
||||
return nil, nil
|
||||
}
|
370
Godeps/_workspace/src/github.com/gin-gonic/gin/gin.go
generated
vendored
370
Godeps/_workspace/src/github.com/gin-gonic/gin/gin.go
generated
vendored
@ -1,370 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin/render"
|
||||
)
|
||||
|
||||
// Version is Framework's version
|
||||
const Version = "v1.0rc2"
|
||||
|
||||
var default404Body = []byte("404 page not found")
|
||||
var default405Body = []byte("405 method not allowed")
|
||||
|
||||
type HandlerFunc func(*Context)
|
||||
type HandlersChain []HandlerFunc
|
||||
|
||||
// Last returns the last handler in the chain. ie. the last handler is the main own.
|
||||
func (c HandlersChain) Last() HandlerFunc {
|
||||
length := len(c)
|
||||
if length > 0 {
|
||||
return c[length-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type (
|
||||
RoutesInfo []RouteInfo
|
||||
RouteInfo struct {
|
||||
Method string
|
||||
Path string
|
||||
Handler string
|
||||
}
|
||||
|
||||
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
|
||||
// Create an instance of Engine, by using New() or Default()
|
||||
Engine struct {
|
||||
RouterGroup
|
||||
HTMLRender render.HTMLRender
|
||||
allNoRoute HandlersChain
|
||||
allNoMethod HandlersChain
|
||||
noRoute HandlersChain
|
||||
noMethod HandlersChain
|
||||
pool sync.Pool
|
||||
trees methodTrees
|
||||
|
||||
// Enables automatic redirection if the current route can't be matched but a
|
||||
// handler for the path with (without) the trailing slash exists.
|
||||
// For example if /foo/ is requested but a route only exists for /foo, the
|
||||
// client is redirected to /foo with http status code 301 for GET requests
|
||||
// and 307 for all other request methods.
|
||||
RedirectTrailingSlash bool
|
||||
|
||||
// If enabled, the router tries to fix the current request path, if no
|
||||
// handle is registered for it.
|
||||
// First superfluous path elements like ../ or // are removed.
|
||||
// Afterwards the router does a case-insensitive lookup of the cleaned path.
|
||||
// If a handle can be found for this route, the router makes a redirection
|
||||
// to the corrected path with status code 301 for GET requests and 307 for
|
||||
// all other request methods.
|
||||
// For example /FOO and /..//Foo could be redirected to /foo.
|
||||
// RedirectTrailingSlash is independent of this option.
|
||||
RedirectFixedPath bool
|
||||
|
||||
// If enabled, the router checks if another method is allowed for the
|
||||
// current route, if the current request can not be routed.
|
||||
// If this is the case, the request is answered with 'Method Not Allowed'
|
||||
// and HTTP status code 405.
|
||||
// If no other Method is allowed, the request is delegated to the NotFound
|
||||
// handler.
|
||||
HandleMethodNotAllowed bool
|
||||
ForwardedByClientIP bool
|
||||
}
|
||||
)
|
||||
|
||||
var _ IRouter = &Engine{}
|
||||
|
||||
// New returns a new blank Engine instance without any middleware attached.
|
||||
// By default the configuration is:
|
||||
// - RedirectTrailingSlash: true
|
||||
// - RedirectFixedPath: false
|
||||
// - HandleMethodNotAllowed: false
|
||||
// - ForwardedByClientIP: true
|
||||
func New() *Engine {
|
||||
debugPrintWARNINGNew()
|
||||
engine := &Engine{
|
||||
RouterGroup: RouterGroup{
|
||||
Handlers: nil,
|
||||
basePath: "/",
|
||||
root: true,
|
||||
},
|
||||
RedirectTrailingSlash: true,
|
||||
RedirectFixedPath: false,
|
||||
HandleMethodNotAllowed: false,
|
||||
ForwardedByClientIP: true,
|
||||
trees: make(methodTrees, 0, 9),
|
||||
}
|
||||
engine.RouterGroup.engine = engine
|
||||
engine.pool.New = func() interface{} {
|
||||
return engine.allocateContext()
|
||||
}
|
||||
return engine
|
||||
}
|
||||
|
||||
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
|
||||
func Default() *Engine {
|
||||
engine := New()
|
||||
engine.Use(Logger(), Recovery())
|
||||
return engine
|
||||
}
|
||||
|
||||
func (engine *Engine) allocateContext() *Context {
|
||||
return &Context{engine: engine}
|
||||
}
|
||||
|
||||
func (engine *Engine) LoadHTMLGlob(pattern string) {
|
||||
if IsDebugging() {
|
||||
debugPrintLoadTemplate(template.Must(template.ParseGlob(pattern)))
|
||||
engine.HTMLRender = render.HTMLDebug{Glob: pattern}
|
||||
} else {
|
||||
templ := template.Must(template.ParseGlob(pattern))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) LoadHTMLFiles(files ...string) {
|
||||
if IsDebugging() {
|
||||
engine.HTMLRender = render.HTMLDebug{Files: files}
|
||||
} else {
|
||||
templ := template.Must(template.ParseFiles(files...))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
||||
if len(engine.trees) > 0 {
|
||||
debugPrintWARNINGSetHTMLTemplate()
|
||||
}
|
||||
engine.HTMLRender = render.HTMLProduction{Template: templ}
|
||||
}
|
||||
|
||||
// NoRoute adds handlers for NoRoute. It return a 404 code by default.
|
||||
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
|
||||
engine.noRoute = handlers
|
||||
engine.rebuild404Handlers()
|
||||
}
|
||||
|
||||
// NoMethod sets the handlers called when... TODO
|
||||
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
|
||||
engine.noMethod = handlers
|
||||
engine.rebuild405Handlers()
|
||||
}
|
||||
|
||||
// Use attachs a global middleware to the router. ie. the middleware attached though Use() will be
|
||||
// included in the handlers chain for every single request. Even 404, 405, static files...
|
||||
// For example, this is the right place for a logger or error management middleware.
|
||||
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
|
||||
engine.RouterGroup.Use(middleware...)
|
||||
engine.rebuild404Handlers()
|
||||
engine.rebuild405Handlers()
|
||||
return engine
|
||||
}
|
||||
|
||||
func (engine *Engine) rebuild404Handlers() {
|
||||
engine.allNoRoute = engine.combineHandlers(engine.noRoute)
|
||||
}
|
||||
|
||||
func (engine *Engine) rebuild405Handlers() {
|
||||
engine.allNoMethod = engine.combineHandlers(engine.noMethod)
|
||||
}
|
||||
|
||||
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
|
||||
assert1(path[0] == '/', "path must begin with '/'")
|
||||
assert1(len(method) > 0, "HTTP method can not be empty")
|
||||
assert1(len(handlers) > 0, "there must be at least one handler")
|
||||
|
||||
debugPrintRoute(method, path, handlers)
|
||||
root := engine.trees.get(method)
|
||||
if root == nil {
|
||||
root = new(node)
|
||||
engine.trees = append(engine.trees, methodTree{method: method, root: root})
|
||||
}
|
||||
root.addRoute(path, handlers)
|
||||
}
|
||||
|
||||
// Routes returns a slice of registered routes, including some useful information, such as:
|
||||
// the http method, path and the handler name.
|
||||
func (engine *Engine) Routes() (routes RoutesInfo) {
|
||||
for _, tree := range engine.trees {
|
||||
routes = iterate("", tree.method, routes, tree.root)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
|
||||
path += root.path
|
||||
if len(root.handlers) > 0 {
|
||||
routes = append(routes, RouteInfo{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Handler: nameOfFunction(root.handlers.Last()),
|
||||
})
|
||||
}
|
||||
for _, child := range root.children {
|
||||
routes = iterate(path, method, routes, child)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
|
||||
// It is a shortcut for http.ListenAndServe(addr, router)
|
||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||
func (engine *Engine) Run(addr ...string) (err error) {
|
||||
defer func() { debugPrintError(err) }()
|
||||
|
||||
address := resolveAddress(addr)
|
||||
debugPrint("Listening and serving HTTP on %s\n", address)
|
||||
err = http.ListenAndServe(address, engine)
|
||||
return
|
||||
}
|
||||
|
||||
// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
|
||||
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||
func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) {
|
||||
debugPrint("Listening and serving HTTPS on %s\n", addr)
|
||||
defer func() { debugPrintError(err) }()
|
||||
|
||||
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
|
||||
return
|
||||
}
|
||||
|
||||
// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
|
||||
// through the specified unix socket (ie. a file).
|
||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||
func (engine *Engine) RunUnix(file string) (err error) {
|
||||
debugPrint("Listening and serving HTTP on unix:/%s", file)
|
||||
defer func() { debugPrintError(err) }()
|
||||
|
||||
os.Remove(file)
|
||||
listener, err := net.Listen("unix", file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer listener.Close()
|
||||
err = http.Serve(listener, engine)
|
||||
return
|
||||
}
|
||||
|
||||
// Conforms to the http.Handler interface.
|
||||
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
c := engine.pool.Get().(*Context)
|
||||
c.writermem.reset(w)
|
||||
c.Request = req
|
||||
c.reset()
|
||||
|
||||
engine.handleHTTPRequest(c)
|
||||
|
||||
engine.pool.Put(c)
|
||||
}
|
||||
|
||||
func (engine *Engine) handleHTTPRequest(context *Context) {
|
||||
httpMethod := context.Request.Method
|
||||
path := context.Request.URL.Path
|
||||
|
||||
// Find root of the tree for the given HTTP method
|
||||
t := engine.trees
|
||||
for i, tl := 0, len(t); i < tl; i++ {
|
||||
if t[i].method == httpMethod {
|
||||
root := t[i].root
|
||||
// Find route in tree
|
||||
handlers, params, tsr := root.getValue(path, context.Params)
|
||||
if handlers != nil {
|
||||
context.handlers = handlers
|
||||
context.Params = params
|
||||
context.Next()
|
||||
context.writermem.WriteHeaderNow()
|
||||
return
|
||||
|
||||
} else if httpMethod != "CONNECT" && path != "/" {
|
||||
if tsr && engine.RedirectTrailingSlash {
|
||||
redirectTrailingSlash(context)
|
||||
return
|
||||
}
|
||||
if engine.RedirectFixedPath && redirectFixedPath(context, root, engine.RedirectFixedPath) {
|
||||
return
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: unit test
|
||||
if engine.HandleMethodNotAllowed {
|
||||
for _, tree := range engine.trees {
|
||||
if tree.method != httpMethod {
|
||||
if handlers, _, _ := tree.root.getValue(path, nil); handlers != nil {
|
||||
context.handlers = engine.allNoMethod
|
||||
serveError(context, 405, default405Body)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
context.handlers = engine.allNoRoute
|
||||
serveError(context, 404, default404Body)
|
||||
}
|
||||
|
||||
var mimePlain = []string{MIMEPlain}
|
||||
|
||||
func serveError(c *Context, code int, defaultMessage []byte) {
|
||||
c.writermem.status = code
|
||||
c.Next()
|
||||
if !c.writermem.Written() {
|
||||
if c.writermem.Status() == code {
|
||||
c.writermem.Header()["Content-Type"] = mimePlain
|
||||
c.Writer.Write(defaultMessage)
|
||||
} else {
|
||||
c.writermem.WriteHeaderNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func redirectTrailingSlash(c *Context) {
|
||||
req := c.Request
|
||||
path := req.URL.Path
|
||||
code := 301 // Permanent redirect, request with GET method
|
||||
if req.Method != "GET" {
|
||||
code = 307
|
||||
}
|
||||
|
||||
if len(path) > 1 && path[len(path)-1] == '/' {
|
||||
req.URL.Path = path[:len(path)-1]
|
||||
} else {
|
||||
req.URL.Path = path + "/"
|
||||
}
|
||||
debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
|
||||
http.Redirect(c.Writer, req, req.URL.String(), code)
|
||||
c.writermem.WriteHeaderNow()
|
||||
}
|
||||
|
||||
func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
|
||||
req := c.Request
|
||||
path := req.URL.Path
|
||||
|
||||
fixedPath, found := root.findCaseInsensitivePath(
|
||||
cleanPath(path),
|
||||
trailingSlash,
|
||||
)
|
||||
if found {
|
||||
code := 301 // Permanent redirect, request with GET method
|
||||
if req.Method != "GET" {
|
||||
code = 307
|
||||
}
|
||||
req.URL.Path = string(fixedPath)
|
||||
debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
|
||||
http.Redirect(c.Writer, req, req.URL.String(), code)
|
||||
c.writermem.WriteHeaderNow()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
123
Godeps/_workspace/src/github.com/gin-gonic/gin/logger.go
generated
vendored
123
Godeps/_workspace/src/github.com/gin-gonic/gin/logger.go
generated
vendored
@ -1,123 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
|
||||
white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
|
||||
yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
|
||||
red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
|
||||
blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109})
|
||||
magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
|
||||
cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
|
||||
reset = string([]byte{27, 91, 48, 109})
|
||||
)
|
||||
|
||||
func ErrorLogger() HandlerFunc {
|
||||
return ErrorLoggerT(ErrorTypeAny)
|
||||
}
|
||||
|
||||
func ErrorLoggerT(typ ErrorType) HandlerFunc {
|
||||
return func(c *Context) {
|
||||
c.Next()
|
||||
errors := c.Errors.ByType(typ)
|
||||
if len(errors) > 0 {
|
||||
c.JSON(-1, errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter
|
||||
// By default gin.DefaultWriter = os.Stdout
|
||||
func Logger() HandlerFunc {
|
||||
return LoggerWithWriter(DefaultWriter)
|
||||
}
|
||||
|
||||
// LoggerWithWriter instance a Logger middleware with the specified writter buffer.
|
||||
// Example: os.Stdout, a file opened in write mode, a socket...
|
||||
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
||||
var skip map[string]struct{}
|
||||
|
||||
if length := len(notlogged); length > 0 {
|
||||
skip = make(map[string]struct{}, length)
|
||||
|
||||
for _, path := range notlogged {
|
||||
skip[path] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return func(c *Context) {
|
||||
// Start timer
|
||||
start := time.Now()
|
||||
path := c.Request.URL.Path
|
||||
|
||||
// Process request
|
||||
c.Next()
|
||||
|
||||
// Log only when path is not being skipped
|
||||
if _, ok := skip[path]; !ok {
|
||||
// Stop timer
|
||||
end := time.Now()
|
||||
latency := end.Sub(start)
|
||||
|
||||
clientIP := c.ClientIP()
|
||||
method := c.Request.Method
|
||||
statusCode := c.Writer.Status()
|
||||
statusColor := colorForStatus(statusCode)
|
||||
methodColor := colorForMethod(method)
|
||||
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
||||
|
||||
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s %s %-7s %s\n%s",
|
||||
end.Format("2006/01/02 - 15:04:05"),
|
||||
statusColor, statusCode, reset,
|
||||
latency,
|
||||
clientIP,
|
||||
methodColor, reset, method,
|
||||
path,
|
||||
comment,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func colorForStatus(code int) string {
|
||||
switch {
|
||||
case code >= 200 && code < 300:
|
||||
return green
|
||||
case code >= 300 && code < 400:
|
||||
return white
|
||||
case code >= 400 && code < 500:
|
||||
return yellow
|
||||
default:
|
||||
return red
|
||||
}
|
||||
}
|
||||
|
||||
func colorForMethod(method string) string {
|
||||
switch method {
|
||||
case "GET":
|
||||
return blue
|
||||
case "POST":
|
||||
return cyan
|
||||
case "PUT":
|
||||
return yellow
|
||||
case "DELETE":
|
||||
return red
|
||||
case "PATCH":
|
||||
return green
|
||||
case "HEAD":
|
||||
return magenta
|
||||
case "OPTIONS":
|
||||
return white
|
||||
default:
|
||||
return reset
|
||||
}
|
||||
}
|
BIN
Godeps/_workspace/src/github.com/gin-gonic/gin/logo.jpg
generated
vendored
BIN
Godeps/_workspace/src/github.com/gin-gonic/gin/logo.jpg
generated
vendored
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
68
Godeps/_workspace/src/github.com/gin-gonic/gin/mode.go
generated
vendored
68
Godeps/_workspace/src/github.com/gin-gonic/gin/mode.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
)
|
||||
|
||||
const ENV_GIN_MODE = "GIN_MODE"
|
||||
|
||||
const (
|
||||
DebugMode string = "debug"
|
||||
ReleaseMode string = "release"
|
||||
TestMode string = "test"
|
||||
)
|
||||
const (
|
||||
debugCode = iota
|
||||
releaseCode = iota
|
||||
testCode = iota
|
||||
)
|
||||
|
||||
// DefaultWriter is the default io.Writer used the Gin for debug output and
|
||||
// middleware output like Logger() or Recovery().
|
||||
// Note that both Logger and Recovery provides custom ways to configure their
|
||||
// output io.Writer.
|
||||
// To support coloring in Windows use:
|
||||
// import "github.com/mattn/go-colorable"
|
||||
// gin.DefaultWriter = colorable.NewColorableStdout()
|
||||
var DefaultWriter = os.Stdout
|
||||
var DefaultErrorWriter = os.Stderr
|
||||
|
||||
var ginMode = debugCode
|
||||
var modeName = DebugMode
|
||||
|
||||
func init() {
|
||||
mode := os.Getenv(ENV_GIN_MODE)
|
||||
if len(mode) == 0 {
|
||||
SetMode(DebugMode)
|
||||
} else {
|
||||
SetMode(mode)
|
||||
}
|
||||
}
|
||||
|
||||
func SetMode(value string) {
|
||||
switch value {
|
||||
case DebugMode:
|
||||
ginMode = debugCode
|
||||
case ReleaseMode:
|
||||
ginMode = releaseCode
|
||||
case TestMode:
|
||||
ginMode = testCode
|
||||
default:
|
||||
panic("gin mode unknown: " + value)
|
||||
}
|
||||
modeName = value
|
||||
}
|
||||
|
||||
func DisableBindValidation() {
|
||||
binding.Validator = nil
|
||||
}
|
||||
|
||||
func Mode() string {
|
||||
return modeName
|
||||
}
|
123
Godeps/_workspace/src/github.com/gin-gonic/gin/path.go
generated
vendored
123
Godeps/_workspace/src/github.com/gin-gonic/gin/path.go
generated
vendored
@ -1,123 +0,0 @@
|
||||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||
// Based on the path package, Copyright 2009 The Go Authors.
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
// CleanPath is the URL version of path.Clean, it returns a canonical URL path
|
||||
// for p, eliminating . and .. elements.
|
||||
//
|
||||
// The following rules are applied iteratively until no further processing can
|
||||
// be done:
|
||||
// 1. Replace multiple slashes with a single slash.
|
||||
// 2. Eliminate each . path name element (the current directory).
|
||||
// 3. Eliminate each inner .. path name element (the parent directory)
|
||||
// along with the non-.. element that precedes it.
|
||||
// 4. Eliminate .. elements that begin a rooted path:
|
||||
// that is, replace "/.." by "/" at the beginning of a path.
|
||||
//
|
||||
// If the result of this process is an empty string, "/" is returned
|
||||
func cleanPath(p string) string {
|
||||
// Turn empty string into "/"
|
||||
if p == "" {
|
||||
return "/"
|
||||
}
|
||||
|
||||
n := len(p)
|
||||
var buf []byte
|
||||
|
||||
// Invariants:
|
||||
// reading from path; r is index of next byte to process.
|
||||
// writing to buf; w is index of next byte to write.
|
||||
|
||||
// path must start with '/'
|
||||
r := 1
|
||||
w := 1
|
||||
|
||||
if p[0] != '/' {
|
||||
r = 0
|
||||
buf = make([]byte, n+1)
|
||||
buf[0] = '/'
|
||||
}
|
||||
|
||||
trailing := n > 2 && p[n-1] == '/'
|
||||
|
||||
// A bit more clunky without a 'lazybuf' like the path package, but the loop
|
||||
// gets completely inlined (bufApp). So in contrast to the path package this
|
||||
// loop has no expensive function calls (except 1x make)
|
||||
|
||||
for r < n {
|
||||
switch {
|
||||
case p[r] == '/':
|
||||
// empty path element, trailing slash is added after the end
|
||||
r++
|
||||
|
||||
case p[r] == '.' && r+1 == n:
|
||||
trailing = true
|
||||
r++
|
||||
|
||||
case p[r] == '.' && p[r+1] == '/':
|
||||
// . element
|
||||
r++
|
||||
|
||||
case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'):
|
||||
// .. element: remove to last /
|
||||
r += 2
|
||||
|
||||
if w > 1 {
|
||||
// can backtrack
|
||||
w--
|
||||
|
||||
if buf == nil {
|
||||
for w > 1 && p[w] != '/' {
|
||||
w--
|
||||
}
|
||||
} else {
|
||||
for w > 1 && buf[w] != '/' {
|
||||
w--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// real path element.
|
||||
// add slash if needed
|
||||
if w > 1 {
|
||||
bufApp(&buf, p, w, '/')
|
||||
w++
|
||||
}
|
||||
|
||||
// copy element
|
||||
for r < n && p[r] != '/' {
|
||||
bufApp(&buf, p, w, p[r])
|
||||
w++
|
||||
r++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// re-append trailing slash
|
||||
if trailing && w > 1 {
|
||||
bufApp(&buf, p, w, '/')
|
||||
w++
|
||||
}
|
||||
|
||||
if buf == nil {
|
||||
return p[:w]
|
||||
}
|
||||
return string(buf[:w])
|
||||
}
|
||||
|
||||
// internal helper to lazily create a buffer if necessary
|
||||
func bufApp(buf *[]byte, s string, w int, c byte) {
|
||||
if *buf == nil {
|
||||
if s[w] == c {
|
||||
return
|
||||
}
|
||||
|
||||
*buf = make([]byte, len(s))
|
||||
copy(*buf, s[:w])
|
||||
}
|
||||
(*buf)[w] = c
|
||||
}
|
108
Godeps/_workspace/src/github.com/gin-gonic/gin/recovery.go
generated
vendored
108
Godeps/_workspace/src/github.com/gin-gonic/gin/recovery.go
generated
vendored
@ -1,108 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http/httputil"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
dunno = []byte("???")
|
||||
centerDot = []byte("·")
|
||||
dot = []byte(".")
|
||||
slash = []byte("/")
|
||||
)
|
||||
|
||||
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
|
||||
func Recovery() HandlerFunc {
|
||||
return RecoveryWithWriter(DefaultErrorWriter)
|
||||
}
|
||||
|
||||
func RecoveryWithWriter(out io.Writer) HandlerFunc {
|
||||
var logger *log.Logger
|
||||
if out != nil {
|
||||
logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags)
|
||||
}
|
||||
return func(c *Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
if logger != nil {
|
||||
stack := stack(3)
|
||||
httprequest, _ := httputil.DumpRequest(c.Request, false)
|
||||
logger.Printf("[Recovery] panic recovered:\n%s\n%s\n%s%s", string(httprequest), err, stack, reset)
|
||||
}
|
||||
c.AbortWithStatus(500)
|
||||
}
|
||||
}()
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// stack returns a nicely formated stack frame, skipping skip frames
|
||||
func stack(skip int) []byte {
|
||||
buf := new(bytes.Buffer) // the returned data
|
||||
// As we loop, we open files and read them. These variables record the currently
|
||||
// loaded file.
|
||||
var lines [][]byte
|
||||
var lastFile string
|
||||
for i := skip; ; i++ { // Skip the expected number of frames
|
||||
pc, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
// Print this much at least. If we can't find the source, it won't show.
|
||||
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
|
||||
if file != lastFile {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
lines = bytes.Split(data, []byte{'\n'})
|
||||
lastFile = file
|
||||
}
|
||||
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// source returns a space-trimmed slice of the n'th line.
|
||||
func source(lines [][]byte, n int) []byte {
|
||||
n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
|
||||
if n < 0 || n >= len(lines) {
|
||||
return dunno
|
||||
}
|
||||
return bytes.TrimSpace(lines[n])
|
||||
}
|
||||
|
||||
// function returns, if possible, the name of the function containing the PC.
|
||||
func function(pc uintptr) []byte {
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
return dunno
|
||||
}
|
||||
name := []byte(fn.Name())
|
||||
// The name includes the path name to the package, which is unnecessary
|
||||
// since the file name is already included. Plus, it has center dots.
|
||||
// That is, we see
|
||||
// runtime/debug.*T·ptrmethod
|
||||
// and want
|
||||
// *T.ptrmethod
|
||||
// Also the package path might contains dot (e.g. code.google.com/...),
|
||||
// so first eliminate the path prefix
|
||||
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
|
||||
name = name[lastslash+1:]
|
||||
}
|
||||
if period := bytes.Index(name, dot); period >= 0 {
|
||||
name = name[period+1:]
|
||||
}
|
||||
name = bytes.Replace(name, centerDot, dot, -1)
|
||||
return name
|
||||
}
|
20
Godeps/_workspace/src/github.com/gin-gonic/gin/render/data.go
generated
vendored
20
Godeps/_workspace/src/github.com/gin-gonic/gin/render/data.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package render
|
||||
|
||||
import "net/http"
|
||||
|
||||
type Data struct {
|
||||
ContentType string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (r Data) Render(w http.ResponseWriter) error {
|
||||
if len(r.ContentType) > 0 {
|
||||
w.Header()["Content-Type"] = []string{r.ContentType}
|
||||
}
|
||||
w.Write(r.Data)
|
||||
return nil
|
||||
}
|
66
Godeps/_workspace/src/github.com/gin-gonic/gin/render/html.go
generated
vendored
66
Godeps/_workspace/src/github.com/gin-gonic/gin/render/html.go
generated
vendored
@ -1,66 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package render
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
HTMLRender interface {
|
||||
Instance(string, interface{}) Render
|
||||
}
|
||||
|
||||
HTMLProduction struct {
|
||||
Template *template.Template
|
||||
}
|
||||
|
||||
HTMLDebug struct {
|
||||
Files []string
|
||||
Glob string
|
||||
}
|
||||
|
||||
HTML struct {
|
||||
Template *template.Template
|
||||
Name string
|
||||
Data interface{}
|
||||
}
|
||||
)
|
||||
|
||||
var htmlContentType = []string{"text/html; charset=utf-8"}
|
||||
|
||||
func (r HTMLProduction) Instance(name string, data interface{}) Render {
|
||||
return HTML{
|
||||
Template: r.Template,
|
||||
Name: name,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (r HTMLDebug) Instance(name string, data interface{}) Render {
|
||||
return HTML{
|
||||
Template: r.loadTemplate(),
|
||||
Name: name,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
func (r HTMLDebug) loadTemplate() *template.Template {
|
||||
if len(r.Files) > 0 {
|
||||
return template.Must(template.ParseFiles(r.Files...))
|
||||
}
|
||||
if len(r.Glob) > 0 {
|
||||
return template.Must(template.ParseGlob(r.Glob))
|
||||
}
|
||||
panic("the HTML debug render was created without files or glob pattern")
|
||||
}
|
||||
|
||||
func (r HTML) Render(w http.ResponseWriter) error {
|
||||
writeContentType(w, htmlContentType)
|
||||
if len(r.Name) == 0 {
|
||||
return r.Template.Execute(w, r.Data)
|
||||
}
|
||||
return r.Template.ExecuteTemplate(w, r.Name, r.Data)
|
||||
}
|
41
Godeps/_workspace/src/github.com/gin-gonic/gin/render/json.go
generated
vendored
41
Godeps/_workspace/src/github.com/gin-gonic/gin/render/json.go
generated
vendored
@ -1,41 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package render
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
JSON struct {
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
IndentedJSON struct {
|
||||
Data interface{}
|
||||
}
|
||||
)
|
||||
|
||||
var jsonContentType = []string{"application/json; charset=utf-8"}
|
||||
|
||||
func (r JSON) Render(w http.ResponseWriter) error {
|
||||
return WriteJSON(w, r.Data)
|
||||
}
|
||||
|
||||
func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
||||
writeContentType(w, jsonContentType)
|
||||
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Write(jsonBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
|
||||
writeContentType(w, jsonContentType)
|
||||
return json.NewEncoder(w).Encode(obj)
|
||||
}
|
24
Godeps/_workspace/src/github.com/gin-gonic/gin/render/redirect.go
generated
vendored
24
Godeps/_workspace/src/github.com/gin-gonic/gin/render/redirect.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Redirect struct {
|
||||
Code int
|
||||
Request *http.Request
|
||||
Location string
|
||||
}
|
||||
|
||||
func (r Redirect) Render(w http.ResponseWriter) error {
|
||||
if (r.Code < 300 || r.Code > 308) && r.Code != 201 {
|
||||
panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code))
|
||||
}
|
||||
http.Redirect(w, r.Request, r.Location, r.Code)
|
||||
return nil
|
||||
}
|
31
Godeps/_workspace/src/github.com/gin-gonic/gin/render/render.go
generated
vendored
31
Godeps/_workspace/src/github.com/gin-gonic/gin/render/render.go
generated
vendored
@ -1,31 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package render
|
||||
|
||||
import "net/http"
|
||||
|
||||
type Render interface {
|
||||
Render(http.ResponseWriter) error
|
||||
}
|
||||
|
||||
var (
|
||||
_ Render = JSON{}
|
||||
_ Render = IndentedJSON{}
|
||||
_ Render = XML{}
|
||||
_ Render = String{}
|
||||
_ Render = Redirect{}
|
||||
_ Render = Data{}
|
||||
_ Render = HTML{}
|
||||
_ HTMLRender = HTMLDebug{}
|
||||
_ HTMLRender = HTMLProduction{}
|
||||
_ Render = YAML{}
|
||||
)
|
||||
|
||||
func writeContentType(w http.ResponseWriter, value []string) {
|
||||
header := w.Header()
|
||||
if val := header["Content-Type"]; len(val) == 0 {
|
||||
header["Content-Type"] = value
|
||||
}
|
||||
}
|
33
Godeps/_workspace/src/github.com/gin-gonic/gin/render/text.go
generated
vendored
33
Godeps/_workspace/src/github.com/gin-gonic/gin/render/text.go
generated
vendored
@ -1,33 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type String struct {
|
||||
Format string
|
||||
Data []interface{}
|
||||
}
|
||||
|
||||
var plainContentType = []string{"text/plain; charset=utf-8"}
|
||||
|
||||
func (r String) Render(w http.ResponseWriter) error {
|
||||
WriteString(w, r.Format, r.Data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
|
||||
writeContentType(w, plainContentType)
|
||||
|
||||
if len(data) > 0 {
|
||||
fmt.Fprintf(w, format, data...)
|
||||
} else {
|
||||
io.WriteString(w, format)
|
||||
}
|
||||
}
|
21
Godeps/_workspace/src/github.com/gin-gonic/gin/render/xml.go
generated
vendored
21
Godeps/_workspace/src/github.com/gin-gonic/gin/render/xml.go
generated
vendored
@ -1,21 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package render
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type XML struct {
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
var xmlContentType = []string{"application/xml; charset=utf-8"}
|
||||
|
||||
func (r XML) Render(w http.ResponseWriter) error {
|
||||
writeContentType(w, xmlContentType)
|
||||
return xml.NewEncoder(w).Encode(r.Data)
|
||||
}
|
29
Godeps/_workspace/src/github.com/gin-gonic/gin/render/yaml.go
generated
vendored
29
Godeps/_workspace/src/github.com/gin-gonic/gin/render/yaml.go
generated
vendored
@ -1,29 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package render
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type YAML struct {
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
|
||||
|
||||
func (r YAML) Render(w http.ResponseWriter) error {
|
||||
writeContentType(w, yamlContentType)
|
||||
|
||||
bytes, err := yaml.Marshal(r.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Write(bytes)
|
||||
return nil
|
||||
}
|
116
Godeps/_workspace/src/github.com/gin-gonic/gin/response_writer.go
generated
vendored
116
Godeps/_workspace/src/github.com/gin-gonic/gin/response_writer.go
generated
vendored
@ -1,116 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
noWritten = -1
|
||||
defaultStatus = 200
|
||||
)
|
||||
|
||||
type (
|
||||
ResponseWriter interface {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Flusher
|
||||
http.CloseNotifier
|
||||
|
||||
// Returns the HTTP response status code of the current request.
|
||||
Status() int
|
||||
|
||||
// Returns the number of bytes already written into the response http body.
|
||||
// See Written()
|
||||
Size() int
|
||||
|
||||
// Writes the string into the response body.
|
||||
WriteString(string) (int, error)
|
||||
|
||||
// Returns true if the response body was already written.
|
||||
Written() bool
|
||||
|
||||
// Forces to write the http header (status code + headers).
|
||||
WriteHeaderNow()
|
||||
}
|
||||
|
||||
responseWriter struct {
|
||||
http.ResponseWriter
|
||||
size int
|
||||
status int
|
||||
}
|
||||
)
|
||||
|
||||
var _ ResponseWriter = &responseWriter{}
|
||||
|
||||
func (w *responseWriter) reset(writer http.ResponseWriter) {
|
||||
w.ResponseWriter = writer
|
||||
w.size = noWritten
|
||||
w.status = defaultStatus
|
||||
}
|
||||
|
||||
func (w *responseWriter) WriteHeader(code int) {
|
||||
if code > 0 && w.status != code {
|
||||
if w.Written() {
|
||||
debugPrint("[WARNING] Headers were already written. Wanted to override status code %d with %d", w.status, code)
|
||||
}
|
||||
w.status = code
|
||||
}
|
||||
}
|
||||
|
||||
func (w *responseWriter) WriteHeaderNow() {
|
||||
if !w.Written() {
|
||||
w.size = 0
|
||||
w.ResponseWriter.WriteHeader(w.status)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *responseWriter) Write(data []byte) (n int, err error) {
|
||||
w.WriteHeaderNow()
|
||||
n, err = w.ResponseWriter.Write(data)
|
||||
w.size += n
|
||||
return
|
||||
}
|
||||
|
||||
func (w *responseWriter) WriteString(s string) (n int, err error) {
|
||||
w.WriteHeaderNow()
|
||||
n, err = io.WriteString(w.ResponseWriter, s)
|
||||
w.size += n
|
||||
return
|
||||
}
|
||||
|
||||
func (w *responseWriter) Status() int {
|
||||
return w.status
|
||||
}
|
||||
|
||||
func (w *responseWriter) Size() int {
|
||||
return w.size
|
||||
}
|
||||
|
||||
func (w *responseWriter) Written() bool {
|
||||
return w.size != noWritten
|
||||
}
|
||||
|
||||
// Implements the http.Hijacker interface
|
||||
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
if w.size < 0 {
|
||||
w.size = 0
|
||||
}
|
||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
// Implements the http.CloseNotify interface
|
||||
func (w *responseWriter) CloseNotify() <-chan bool {
|
||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// Implements the http.Flush interface
|
||||
func (w *responseWriter) Flush() {
|
||||
w.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
215
Godeps/_workspace/src/github.com/gin-gonic/gin/routergroup.go
generated
vendored
215
Godeps/_workspace/src/github.com/gin-gonic/gin/routergroup.go
generated
vendored
@ -1,215 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
IRouter interface {
|
||||
IRoutes
|
||||
Group(string, ...HandlerFunc) *RouterGroup
|
||||
}
|
||||
|
||||
IRoutes interface {
|
||||
Use(...HandlerFunc) IRoutes
|
||||
|
||||
Handle(string, string, ...HandlerFunc) IRoutes
|
||||
Any(string, ...HandlerFunc) IRoutes
|
||||
GET(string, ...HandlerFunc) IRoutes
|
||||
POST(string, ...HandlerFunc) IRoutes
|
||||
DELETE(string, ...HandlerFunc) IRoutes
|
||||
PATCH(string, ...HandlerFunc) IRoutes
|
||||
PUT(string, ...HandlerFunc) IRoutes
|
||||
OPTIONS(string, ...HandlerFunc) IRoutes
|
||||
HEAD(string, ...HandlerFunc) IRoutes
|
||||
|
||||
StaticFile(string, string) IRoutes
|
||||
Static(string, string) IRoutes
|
||||
StaticFS(string, http.FileSystem) IRoutes
|
||||
}
|
||||
|
||||
// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
|
||||
// and an array of handlers (middleware)
|
||||
RouterGroup struct {
|
||||
Handlers HandlersChain
|
||||
basePath string
|
||||
engine *Engine
|
||||
root bool
|
||||
}
|
||||
)
|
||||
|
||||
var _ IRouter = &RouterGroup{}
|
||||
|
||||
// Use adds middleware to the group, see example code in github.
|
||||
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
|
||||
group.Handlers = append(group.Handlers, middleware...)
|
||||
return group.returnObj()
|
||||
}
|
||||
|
||||
// Group creates a new router group. You should add all the routes that have common middlwares or the same path prefix.
|
||||
// For example, all the routes that use a common middlware for authorization could be grouped.
|
||||
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
|
||||
return &RouterGroup{
|
||||
Handlers: group.combineHandlers(handlers),
|
||||
basePath: group.calculateAbsolutePath(relativePath),
|
||||
engine: group.engine,
|
||||
}
|
||||
}
|
||||
|
||||
func (group *RouterGroup) BasePath() string {
|
||||
return group.basePath
|
||||
}
|
||||
|
||||
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
|
||||
absolutePath := group.calculateAbsolutePath(relativePath)
|
||||
handlers = group.combineHandlers(handlers)
|
||||
group.engine.addRoute(httpMethod, absolutePath, handlers)
|
||||
return group.returnObj()
|
||||
}
|
||||
|
||||
// Handle registers a new request handle and middleware with the given path and method.
|
||||
// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
|
||||
// See the example code in github.
|
||||
//
|
||||
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
|
||||
// functions can be used.
|
||||
//
|
||||
// This function is intended for bulk loading and to allow the usage of less
|
||||
// frequently used, non-standardized or custom methods (e.g. for internal
|
||||
// communication with a proxy).
|
||||
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||
if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
|
||||
panic("http method " + httpMethod + " is not valid")
|
||||
}
|
||||
return group.handle(httpMethod, relativePath, handlers)
|
||||
}
|
||||
|
||||
// POST is a shortcut for router.Handle("POST", path, handle)
|
||||
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||
return group.handle("POST", relativePath, handlers)
|
||||
}
|
||||
|
||||
// GET is a shortcut for router.Handle("GET", path, handle)
|
||||
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||
return group.handle("GET", relativePath, handlers)
|
||||
}
|
||||
|
||||
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
|
||||
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||
return group.handle("DELETE", relativePath, handlers)
|
||||
}
|
||||
|
||||
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
|
||||
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||
return group.handle("PATCH", relativePath, handlers)
|
||||
}
|
||||
|
||||
// PUT is a shortcut for router.Handle("PUT", path, handle)
|
||||
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||
return group.handle("PUT", relativePath, handlers)
|
||||
}
|
||||
|
||||
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
|
||||
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||
return group.handle("OPTIONS", relativePath, handlers)
|
||||
}
|
||||
|
||||
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
|
||||
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||
return group.handle("HEAD", relativePath, handlers)
|
||||
}
|
||||
|
||||
// Any registers a route that matches all the HTTP methods.
|
||||
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE
|
||||
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||
group.handle("GET", relativePath, handlers)
|
||||
group.handle("POST", relativePath, handlers)
|
||||
group.handle("PUT", relativePath, handlers)
|
||||
group.handle("PATCH", relativePath, handlers)
|
||||
group.handle("HEAD", relativePath, handlers)
|
||||
group.handle("OPTIONS", relativePath, handlers)
|
||||
group.handle("DELETE", relativePath, handlers)
|
||||
group.handle("CONNECT", relativePath, handlers)
|
||||
group.handle("TRACE", relativePath, handlers)
|
||||
return group.returnObj()
|
||||
}
|
||||
|
||||
// StaticFile registers a single route in order to server a single file of the local filesystem.
|
||||
// router.StaticFile("favicon.ico", "./resources/favicon.ico")
|
||||
func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
|
||||
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
|
||||
panic("URL parameters can not be used when serving a static file")
|
||||
}
|
||||
handler := func(c *Context) {
|
||||
c.File(filepath)
|
||||
}
|
||||
group.GET(relativePath, handler)
|
||||
group.HEAD(relativePath, handler)
|
||||
return group.returnObj()
|
||||
}
|
||||
|
||||
// Static serves files from the given file system root.
|
||||
// Internally a http.FileServer is used, therefore http.NotFound is used instead
|
||||
// of the Router's NotFound handler.
|
||||
// To use the operating system's file system implementation,
|
||||
// use :
|
||||
// router.Static("/static", "/var/www")
|
||||
func (group *RouterGroup) Static(relativePath, root string) IRoutes {
|
||||
return group.StaticFS(relativePath, Dir(root, false))
|
||||
}
|
||||
|
||||
// StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead.
|
||||
// Gin by default user: gin.Dir()
|
||||
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
|
||||
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
|
||||
panic("URL parameters can not be used when serving a static folder")
|
||||
}
|
||||
handler := group.createStaticHandler(relativePath, fs)
|
||||
urlPattern := path.Join(relativePath, "/*filepath")
|
||||
|
||||
// Register GET and HEAD handlers
|
||||
group.GET(urlPattern, handler)
|
||||
group.HEAD(urlPattern, handler)
|
||||
return group.returnObj()
|
||||
}
|
||||
|
||||
func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
|
||||
absolutePath := group.calculateAbsolutePath(relativePath)
|
||||
fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
|
||||
_, nolisting := fs.(*onlyfilesFS)
|
||||
return func(c *Context) {
|
||||
if nolisting {
|
||||
c.Writer.WriteHeader(404)
|
||||
}
|
||||
fileServer.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
|
||||
finalSize := len(group.Handlers) + len(handlers)
|
||||
if finalSize >= int(abortIndex) {
|
||||
panic("too many handlers")
|
||||
}
|
||||
mergedHandlers := make(HandlersChain, finalSize)
|
||||
copy(mergedHandlers, group.Handlers)
|
||||
copy(mergedHandlers[len(group.Handlers):], handlers)
|
||||
return mergedHandlers
|
||||
}
|
||||
|
||||
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
|
||||
return joinPaths(group.basePath, relativePath)
|
||||
}
|
||||
|
||||
func (group *RouterGroup) returnObj() IRoutes {
|
||||
if group.root {
|
||||
return group.engine
|
||||
}
|
||||
return group
|
||||
}
|
605
Godeps/_workspace/src/github.com/gin-gonic/gin/tree.go
generated
vendored
605
Godeps/_workspace/src/github.com/gin-gonic/gin/tree.go
generated
vendored
@ -1,605 +0,0 @@
|
||||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Param is a single URL parameter, consisting of a key and a value.
|
||||
type Param struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// Params is a Param-slice, as returned by the router.
|
||||
// The slice is ordered, the first URL parameter is also the first slice value.
|
||||
// It is therefore safe to read values by the index.
|
||||
type Params []Param
|
||||
|
||||
// Get returns the value of the first Param which key matches the given name.
|
||||
// If no matching Param is found, an empty string is returned.
|
||||
func (ps Params) Get(name string) (string, bool) {
|
||||
for _, entry := range ps {
|
||||
if entry.Key == name {
|
||||
return entry.Value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// ByName returns the value of the first Param which key matches the given name.
|
||||
// If no matching Param is found, an empty string is returned.
|
||||
func (ps Params) ByName(name string) (va string) {
|
||||
va, _ = ps.Get(name)
|
||||
return
|
||||
}
|
||||
|
||||
type methodTree struct {
|
||||
method string
|
||||
root *node
|
||||
}
|
||||
|
||||
type methodTrees []methodTree
|
||||
|
||||
func (trees methodTrees) get(method string) *node {
|
||||
for _, tree := range trees {
|
||||
if tree.method == method {
|
||||
return tree.root
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a <= b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func countParams(path string) uint8 {
|
||||
var n uint
|
||||
for i := 0; i < len(path); i++ {
|
||||
if path[i] != ':' && path[i] != '*' {
|
||||
continue
|
||||
}
|
||||
n++
|
||||
}
|
||||
if n >= 255 {
|
||||
return 255
|
||||
}
|
||||
return uint8(n)
|
||||
}
|
||||
|
||||
type nodeType uint8
|
||||
|
||||
const (
|
||||
static nodeType = iota // default
|
||||
root
|
||||
param
|
||||
catchAll
|
||||
)
|
||||
|
||||
type node struct {
|
||||
path string
|
||||
wildChild bool
|
||||
nType nodeType
|
||||
maxParams uint8
|
||||
indices string
|
||||
children []*node
|
||||
handlers HandlersChain
|
||||
priority uint32
|
||||
}
|
||||
|
||||
// increments priority of the given child and reorders if necessary
|
||||
func (n *node) incrementChildPrio(pos int) int {
|
||||
n.children[pos].priority++
|
||||
prio := n.children[pos].priority
|
||||
|
||||
// adjust position (move to front)
|
||||
newPos := pos
|
||||
for newPos > 0 && n.children[newPos-1].priority < prio {
|
||||
// swap node positions
|
||||
tmpN := n.children[newPos-1]
|
||||
n.children[newPos-1] = n.children[newPos]
|
||||
n.children[newPos] = tmpN
|
||||
|
||||
newPos--
|
||||
}
|
||||
|
||||
// build new index char string
|
||||
if newPos != pos {
|
||||
n.indices = n.indices[:newPos] + // unchanged prefix, might be empty
|
||||
n.indices[pos:pos+1] + // the index char we move
|
||||
n.indices[newPos:pos] + n.indices[pos+1:] // rest without char at 'pos'
|
||||
}
|
||||
|
||||
return newPos
|
||||
}
|
||||
|
||||
// addRoute adds a node with the given handle to the path.
|
||||
// Not concurrency-safe!
|
||||
func (n *node) addRoute(path string, handlers HandlersChain) {
|
||||
fullPath := path
|
||||
n.priority++
|
||||
numParams := countParams(path)
|
||||
|
||||
// non-empty tree
|
||||
if len(n.path) > 0 || len(n.children) > 0 {
|
||||
walk:
|
||||
for {
|
||||
// Update maxParams of the current node
|
||||
if numParams > n.maxParams {
|
||||
n.maxParams = numParams
|
||||
}
|
||||
|
||||
// Find the longest common prefix.
|
||||
// This also implies that the common prefix contains no ':' or '*'
|
||||
// since the existing key can't contain those chars.
|
||||
i := 0
|
||||
max := min(len(path), len(n.path))
|
||||
for i < max && path[i] == n.path[i] {
|
||||
i++
|
||||
}
|
||||
|
||||
// Split edge
|
||||
if i < len(n.path) {
|
||||
child := node{
|
||||
path: n.path[i:],
|
||||
wildChild: n.wildChild,
|
||||
indices: n.indices,
|
||||
children: n.children,
|
||||
handlers: n.handlers,
|
||||
priority: n.priority - 1,
|
||||
}
|
||||
|
||||
// Update maxParams (max of all children)
|
||||
for i := range child.children {
|
||||
if child.children[i].maxParams > child.maxParams {
|
||||
child.maxParams = child.children[i].maxParams
|
||||
}
|
||||
}
|
||||
|
||||
n.children = []*node{&child}
|
||||
// []byte for proper unicode char conversion, see #65
|
||||
n.indices = string([]byte{n.path[i]})
|
||||
n.path = path[:i]
|
||||
n.handlers = nil
|
||||
n.wildChild = false
|
||||
}
|
||||
|
||||
// Make new node a child of this node
|
||||
if i < len(path) {
|
||||
path = path[i:]
|
||||
|
||||
if n.wildChild {
|
||||
n = n.children[0]
|
||||
n.priority++
|
||||
|
||||
// Update maxParams of the child node
|
||||
if numParams > n.maxParams {
|
||||
n.maxParams = numParams
|
||||
}
|
||||
numParams--
|
||||
|
||||
// Check if the wildcard matches
|
||||
if len(path) >= len(n.path) && n.path == path[:len(n.path)] {
|
||||
// check for longer wildcard, e.g. :name and :names
|
||||
if len(n.path) >= len(path) || path[len(n.path)] == '/' {
|
||||
continue walk
|
||||
}
|
||||
}
|
||||
|
||||
panic("path segment '" + path +
|
||||
"' conflicts with existing wildcard '" + n.path +
|
||||
"' in path '" + fullPath + "'")
|
||||
}
|
||||
|
||||
c := path[0]
|
||||
|
||||
// slash after param
|
||||
if n.nType == param && c == '/' && len(n.children) == 1 {
|
||||
n = n.children[0]
|
||||
n.priority++
|
||||
continue walk
|
||||
}
|
||||
|
||||
// Check if a child with the next path byte exists
|
||||
for i := 0; i < len(n.indices); i++ {
|
||||
if c == n.indices[i] {
|
||||
i = n.incrementChildPrio(i)
|
||||
n = n.children[i]
|
||||
continue walk
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise insert it
|
||||
if c != ':' && c != '*' {
|
||||
// []byte for proper unicode char conversion, see #65
|
||||
n.indices += string([]byte{c})
|
||||
child := &node{
|
||||
maxParams: numParams,
|
||||
}
|
||||
n.children = append(n.children, child)
|
||||
n.incrementChildPrio(len(n.indices) - 1)
|
||||
n = child
|
||||
}
|
||||
n.insertChild(numParams, path, fullPath, handlers)
|
||||
return
|
||||
|
||||
} else if i == len(path) { // Make node a (in-path) leaf
|
||||
if n.handlers != nil {
|
||||
panic("handlers are already registered for path ''" + fullPath + "'")
|
||||
}
|
||||
n.handlers = handlers
|
||||
}
|
||||
return
|
||||
}
|
||||
} else { // Empty tree
|
||||
n.insertChild(numParams, path, fullPath, handlers)
|
||||
n.nType = root
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) {
|
||||
var offset int // already handled bytes of the path
|
||||
|
||||
// find prefix until first wildcard (beginning with ':'' or '*'')
|
||||
for i, max := 0, len(path); numParams > 0; i++ {
|
||||
c := path[i]
|
||||
if c != ':' && c != '*' {
|
||||
continue
|
||||
}
|
||||
|
||||
// find wildcard end (either '/' or path end)
|
||||
end := i + 1
|
||||
for end < max && path[end] != '/' {
|
||||
switch path[end] {
|
||||
// the wildcard name must not contain ':' and '*'
|
||||
case ':', '*':
|
||||
panic("only one wildcard per path segment is allowed, has: '" +
|
||||
path[i:] + "' in path '" + fullPath + "'")
|
||||
default:
|
||||
end++
|
||||
}
|
||||
}
|
||||
|
||||
// check if this Node existing children which would be
|
||||
// unreachable if we insert the wildcard here
|
||||
if len(n.children) > 0 {
|
||||
panic("wildcard route '" + path[i:end] +
|
||||
"' conflicts with existing children in path '" + fullPath + "'")
|
||||
}
|
||||
|
||||
// check if the wildcard has a name
|
||||
if end-i < 2 {
|
||||
panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
|
||||
}
|
||||
|
||||
if c == ':' { // param
|
||||
// split path at the beginning of the wildcard
|
||||
if i > 0 {
|
||||
n.path = path[offset:i]
|
||||
offset = i
|
||||
}
|
||||
|
||||
child := &node{
|
||||
nType: param,
|
||||
maxParams: numParams,
|
||||
}
|
||||
n.children = []*node{child}
|
||||
n.wildChild = true
|
||||
n = child
|
||||
n.priority++
|
||||
numParams--
|
||||
|
||||
// if the path doesn't end with the wildcard, then there
|
||||
// will be another non-wildcard subpath starting with '/'
|
||||
if end < max {
|
||||
n.path = path[offset:end]
|
||||
offset = end
|
||||
|
||||
child := &node{
|
||||
maxParams: numParams,
|
||||
priority: 1,
|
||||
}
|
||||
n.children = []*node{child}
|
||||
n = child
|
||||
}
|
||||
|
||||
} else { // catchAll
|
||||
if end != max || numParams > 1 {
|
||||
panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
|
||||
}
|
||||
|
||||
if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
|
||||
panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
|
||||
}
|
||||
|
||||
// currently fixed width 1 for '/'
|
||||
i--
|
||||
if path[i] != '/' {
|
||||
panic("no / before catch-all in path '" + fullPath + "'")
|
||||
}
|
||||
|
||||
n.path = path[offset:i]
|
||||
|
||||
// first node: catchAll node with empty path
|
||||
child := &node{
|
||||
wildChild: true,
|
||||
nType: catchAll,
|
||||
maxParams: 1,
|
||||
}
|
||||
n.children = []*node{child}
|
||||
n.indices = string(path[i])
|
||||
n = child
|
||||
n.priority++
|
||||
|
||||
// second node: node holding the variable
|
||||
child = &node{
|
||||
path: path[i:],
|
||||
nType: catchAll,
|
||||
maxParams: 1,
|
||||
handlers: handlers,
|
||||
priority: 1,
|
||||
}
|
||||
n.children = []*node{child}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// insert remaining path part and handle to the leaf
|
||||
n.path = path[offset:]
|
||||
n.handlers = handlers
|
||||
}
|
||||
|
||||
// Returns the handle registered with the given path (key). The values of
|
||||
// wildcards are saved to a map.
|
||||
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
|
||||
// made if a handle exists with an extra (without the) trailing slash for the
|
||||
// given path.
|
||||
func (n *node) getValue(path string, po Params) (handlers HandlersChain, p Params, tsr bool) {
|
||||
p = po
|
||||
walk: // Outer loop for walking the tree
|
||||
for {
|
||||
if len(path) > len(n.path) {
|
||||
if path[:len(n.path)] == n.path {
|
||||
path = path[len(n.path):]
|
||||
// If this node does not have a wildcard (param or catchAll)
|
||||
// child, we can just look up the next child node and continue
|
||||
// to walk down the tree
|
||||
if !n.wildChild {
|
||||
c := path[0]
|
||||
for i := 0; i < len(n.indices); i++ {
|
||||
if c == n.indices[i] {
|
||||
n = n.children[i]
|
||||
continue walk
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found.
|
||||
// We can recommend to redirect to the same URL without a
|
||||
// trailing slash if a leaf exists for that path.
|
||||
tsr = (path == "/" && n.handlers != nil)
|
||||
return
|
||||
}
|
||||
|
||||
// handle wildcard child
|
||||
n = n.children[0]
|
||||
switch n.nType {
|
||||
case param:
|
||||
// find param end (either '/' or path end)
|
||||
end := 0
|
||||
for end < len(path) && path[end] != '/' {
|
||||
end++
|
||||
}
|
||||
|
||||
// save param value
|
||||
if cap(p) < int(n.maxParams) {
|
||||
p = make(Params, 0, n.maxParams)
|
||||
}
|
||||
i := len(p)
|
||||
p = p[:i+1] // expand slice within preallocated capacity
|
||||
p[i].Key = n.path[1:]
|
||||
p[i].Value = path[:end]
|
||||
|
||||
// we need to go deeper!
|
||||
if end < len(path) {
|
||||
if len(n.children) > 0 {
|
||||
path = path[end:]
|
||||
n = n.children[0]
|
||||
continue walk
|
||||
}
|
||||
|
||||
// ... but we can't
|
||||
tsr = (len(path) == end+1)
|
||||
return
|
||||
}
|
||||
|
||||
if handlers = n.handlers; handlers != nil {
|
||||
return
|
||||
} else if len(n.children) == 1 {
|
||||
// No handle found. Check if a handle for this path + a
|
||||
// trailing slash exists for TSR recommendation
|
||||
n = n.children[0]
|
||||
tsr = (n.path == "/" && n.handlers != nil)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case catchAll:
|
||||
// save param value
|
||||
if cap(p) < int(n.maxParams) {
|
||||
p = make(Params, 0, n.maxParams)
|
||||
}
|
||||
i := len(p)
|
||||
p = p[:i+1] // expand slice within preallocated capacity
|
||||
p[i].Key = n.path[2:]
|
||||
p[i].Value = path
|
||||
|
||||
handlers = n.handlers
|
||||
return
|
||||
|
||||
default:
|
||||
panic("invalid node type")
|
||||
}
|
||||
}
|
||||
} else if path == n.path {
|
||||
// We should have reached the node containing the handle.
|
||||
// Check if this node has a handle registered.
|
||||
if handlers = n.handlers; handlers != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if path == "/" && n.wildChild && n.nType != root {
|
||||
tsr = true
|
||||
return
|
||||
}
|
||||
|
||||
// No handle found. Check if a handle for this path + a
|
||||
// trailing slash exists for trailing slash recommendation
|
||||
for i := 0; i < len(n.indices); i++ {
|
||||
if n.indices[i] == '/' {
|
||||
n = n.children[i]
|
||||
tsr = (len(n.path) == 1 && n.handlers != nil) ||
|
||||
(n.nType == catchAll && n.children[0].handlers != nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Nothing found. We can recommend to redirect to the same URL with an
|
||||
// extra trailing slash if a leaf exists for that path
|
||||
tsr = (path == "/") ||
|
||||
(len(n.path) == len(path)+1 && n.path[len(path)] == '/' &&
|
||||
path == n.path[:len(n.path)-1] && n.handlers != nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Makes a case-insensitive lookup of the given path and tries to find a handler.
|
||||
// It can optionally also fix trailing slashes.
|
||||
// It returns the case-corrected path and a bool indicating whether the lookup
|
||||
// was successful.
|
||||
func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPath []byte, found bool) {
|
||||
ciPath = make([]byte, 0, len(path)+1) // preallocate enough memory
|
||||
|
||||
// Outer loop for walking the tree
|
||||
for len(path) >= len(n.path) && strings.ToLower(path[:len(n.path)]) == strings.ToLower(n.path) {
|
||||
path = path[len(n.path):]
|
||||
ciPath = append(ciPath, n.path...)
|
||||
|
||||
if len(path) > 0 {
|
||||
// If this node does not have a wildcard (param or catchAll) child,
|
||||
// we can just look up the next child node and continue to walk down
|
||||
// the tree
|
||||
if !n.wildChild {
|
||||
r := unicode.ToLower(rune(path[0]))
|
||||
for i, index := range n.indices {
|
||||
// must use recursive approach since both index and
|
||||
// ToLower(index) could exist. We must check both.
|
||||
if r == unicode.ToLower(index) {
|
||||
out, found := n.children[i].findCaseInsensitivePath(path, fixTrailingSlash)
|
||||
if found {
|
||||
return append(ciPath, out...), true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found. We can recommend to redirect to the same URL
|
||||
// without a trailing slash if a leaf exists for that path
|
||||
found = (fixTrailingSlash && path == "/" && n.handlers != nil)
|
||||
return
|
||||
}
|
||||
|
||||
n = n.children[0]
|
||||
switch n.nType {
|
||||
case param:
|
||||
// find param end (either '/' or path end)
|
||||
k := 0
|
||||
for k < len(path) && path[k] != '/' {
|
||||
k++
|
||||
}
|
||||
|
||||
// add param value to case insensitive path
|
||||
ciPath = append(ciPath, path[:k]...)
|
||||
|
||||
// we need to go deeper!
|
||||
if k < len(path) {
|
||||
if len(n.children) > 0 {
|
||||
path = path[k:]
|
||||
n = n.children[0]
|
||||
continue
|
||||
}
|
||||
|
||||
// ... but we can't
|
||||
if fixTrailingSlash && len(path) == k+1 {
|
||||
return ciPath, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if n.handlers != nil {
|
||||
return ciPath, true
|
||||
} else if fixTrailingSlash && len(n.children) == 1 {
|
||||
// No handle found. Check if a handle for this path + a
|
||||
// trailing slash exists
|
||||
n = n.children[0]
|
||||
if n.path == "/" && n.handlers != nil {
|
||||
return append(ciPath, '/'), true
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
case catchAll:
|
||||
return append(ciPath, path...), true
|
||||
|
||||
default:
|
||||
panic("invalid node type")
|
||||
}
|
||||
} else {
|
||||
// We should have reached the node containing the handle.
|
||||
// Check if this node has a handle registered.
|
||||
if n.handlers != nil {
|
||||
return ciPath, true
|
||||
}
|
||||
|
||||
// No handle found.
|
||||
// Try to fix the path by adding a trailing slash
|
||||
if fixTrailingSlash {
|
||||
for i := 0; i < len(n.indices); i++ {
|
||||
if n.indices[i] == '/' {
|
||||
n = n.children[i]
|
||||
if (len(n.path) == 1 && n.handlers != nil) ||
|
||||
(n.nType == catchAll && n.children[0].handlers != nil) {
|
||||
return append(ciPath, '/'), true
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found.
|
||||
// Try to fix the path by adding / removing a trailing slash
|
||||
if fixTrailingSlash {
|
||||
if path == "/" {
|
||||
return ciPath, true
|
||||
}
|
||||
if len(path)+1 == len(n.path) && n.path[len(path)] == '/' &&
|
||||
strings.ToLower(path) == strings.ToLower(n.path[:len(path)]) &&
|
||||
n.handlers != nil {
|
||||
return append(ciPath, n.path...), true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
154
Godeps/_workspace/src/github.com/gin-gonic/gin/utils.go
generated
vendored
154
Godeps/_workspace/src/github.com/gin-gonic/gin/utils.go
generated
vendored
@ -1,154 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const BindKey = "_gin-gonic/gin/bindkey"
|
||||
|
||||
func Bind(val interface{}) HandlerFunc {
|
||||
value := reflect.ValueOf(val)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
panic(`Bind struct can not be a pointer. Example:
|
||||
Use: gin.Bind(Struct{}) instead of gin.Bind(&Struct{})
|
||||
`)
|
||||
}
|
||||
typ := value.Type()
|
||||
|
||||
return func(c *Context) {
|
||||
obj := reflect.New(typ).Interface()
|
||||
if c.Bind(obj) == nil {
|
||||
c.Set(BindKey, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WrapF(f http.HandlerFunc) HandlerFunc {
|
||||
return func(c *Context) {
|
||||
f(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func WrapH(h http.Handler) HandlerFunc {
|
||||
return func(c *Context) {
|
||||
h.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
|
||||
type H map[string]interface{}
|
||||
|
||||
// MarshalXML allows type H to be used with xml.Marshal
|
||||
func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
start.Name = xml.Name{
|
||||
Space: "",
|
||||
Local: "map",
|
||||
}
|
||||
if err := e.EncodeToken(start); err != nil {
|
||||
return err
|
||||
}
|
||||
for key, value := range h {
|
||||
elem := xml.StartElement{
|
||||
Name: xml.Name{Space: "", Local: key},
|
||||
Attr: []xml.Attr{},
|
||||
}
|
||||
if err := e.EncodeElement(value, elem); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func assert1(guard bool, text string) {
|
||||
if !guard {
|
||||
panic(text)
|
||||
}
|
||||
}
|
||||
|
||||
func filterFlags(content string) string {
|
||||
for i, char := range content {
|
||||
if char == ' ' || char == ';' {
|
||||
return content[:i]
|
||||
}
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
func chooseData(custom, wildcard interface{}) interface{} {
|
||||
if custom == nil {
|
||||
if wildcard == nil {
|
||||
panic("negotiation config is invalid")
|
||||
}
|
||||
return wildcard
|
||||
}
|
||||
return custom
|
||||
}
|
||||
|
||||
func parseAccept(acceptHeader string) []string {
|
||||
parts := strings.Split(acceptHeader, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
index := strings.IndexByte(part, ';')
|
||||
if index >= 0 {
|
||||
part = part[0:index]
|
||||
}
|
||||
part = strings.TrimSpace(part)
|
||||
if len(part) > 0 {
|
||||
out = append(out, part)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func lastChar(str string) uint8 {
|
||||
size := len(str)
|
||||
if size == 0 {
|
||||
panic("The length of the string can't be 0")
|
||||
}
|
||||
return str[size-1]
|
||||
}
|
||||
|
||||
func nameOfFunction(f interface{}) string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
|
||||
}
|
||||
|
||||
func joinPaths(absolutePath, relativePath string) string {
|
||||
if len(relativePath) == 0 {
|
||||
return absolutePath
|
||||
}
|
||||
|
||||
finalPath := path.Join(absolutePath, relativePath)
|
||||
appendSlash := lastChar(relativePath) == '/' && lastChar(finalPath) != '/'
|
||||
if appendSlash {
|
||||
return finalPath + "/"
|
||||
}
|
||||
return finalPath
|
||||
}
|
||||
|
||||
func resolveAddress(addr []string) string {
|
||||
switch len(addr) {
|
||||
case 0:
|
||||
if port := os.Getenv("PORT"); len(port) > 0 {
|
||||
debugPrint("Environment variable PORT=\"%s\"", port)
|
||||
return ":" + port
|
||||
}
|
||||
debugPrint("Environment variable PORT is undefined. Using port :8080 by default")
|
||||
return ":8080"
|
||||
case 1:
|
||||
return addr[0]
|
||||
default:
|
||||
panic("too much parameters")
|
||||
}
|
||||
}
|
1
Godeps/_workspace/src/github.com/gin-gonic/gin/wercker.yml
generated
vendored
1
Godeps/_workspace/src/github.com/gin-gonic/gin/wercker.yml
generated
vendored
@ -1 +0,0 @@
|
||||
box: wercker/default
|
31
Godeps/_workspace/src/github.com/golang/protobuf/LICENSE
generated
vendored
31
Godeps/_workspace/src/github.com/golang/protobuf/LICENSE
generated
vendored
@ -1,31 +0,0 @@
|
||||
Go support for Protocol Buffers - Google's data interchange format
|
||||
|
||||
Copyright 2010 The Go Authors. All rights reserved.
|
||||
https://github.com/golang/protobuf
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
43
Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile
generated
vendored
43
Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile
generated
vendored
@ -1,43 +0,0 @@
|
||||
# Go support for Protocol Buffers - Google's data interchange format
|
||||
#
|
||||
# Copyright 2010 The Go Authors. All rights reserved.
|
||||
# https://github.com/golang/protobuf
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
install:
|
||||
go install
|
||||
|
||||
test: install generate-test-pbs
|
||||
go test
|
||||
|
||||
|
||||
generate-test-pbs:
|
||||
make install
|
||||
make -C testdata
|
||||
protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto
|
||||
make
|
223
Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go
generated
vendored
223
Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go
generated
vendored
@ -1,223 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Protocol buffer deep copy and merge.
|
||||
// TODO: RawMessage.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Clone returns a deep copy of a protocol buffer.
|
||||
func Clone(pb Message) Message {
|
||||
in := reflect.ValueOf(pb)
|
||||
if in.IsNil() {
|
||||
return pb
|
||||
}
|
||||
|
||||
out := reflect.New(in.Type().Elem())
|
||||
// out is empty so a merge is a deep copy.
|
||||
mergeStruct(out.Elem(), in.Elem())
|
||||
return out.Interface().(Message)
|
||||
}
|
||||
|
||||
// Merge merges src into dst.
|
||||
// Required and optional fields that are set in src will be set to that value in dst.
|
||||
// Elements of repeated fields will be appended.
|
||||
// Merge panics if src and dst are not the same type, or if dst is nil.
|
||||
func Merge(dst, src Message) {
|
||||
in := reflect.ValueOf(src)
|
||||
out := reflect.ValueOf(dst)
|
||||
if out.IsNil() {
|
||||
panic("proto: nil destination")
|
||||
}
|
||||
if in.Type() != out.Type() {
|
||||
// Explicit test prior to mergeStruct so that mistyped nils will fail
|
||||
panic("proto: type mismatch")
|
||||
}
|
||||
if in.IsNil() {
|
||||
// Merging nil into non-nil is a quiet no-op
|
||||
return
|
||||
}
|
||||
mergeStruct(out.Elem(), in.Elem())
|
||||
}
|
||||
|
||||
func mergeStruct(out, in reflect.Value) {
|
||||
sprop := GetProperties(in.Type())
|
||||
for i := 0; i < in.NumField(); i++ {
|
||||
f := in.Type().Field(i)
|
||||
if strings.HasPrefix(f.Name, "XXX_") {
|
||||
continue
|
||||
}
|
||||
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
|
||||
}
|
||||
|
||||
if emIn, ok := in.Addr().Interface().(extendableProto); ok {
|
||||
emOut := out.Addr().Interface().(extendableProto)
|
||||
mergeExtension(emOut.ExtensionMap(), emIn.ExtensionMap())
|
||||
}
|
||||
|
||||
uf := in.FieldByName("XXX_unrecognized")
|
||||
if !uf.IsValid() {
|
||||
return
|
||||
}
|
||||
uin := uf.Bytes()
|
||||
if len(uin) > 0 {
|
||||
out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...))
|
||||
}
|
||||
}
|
||||
|
||||
// mergeAny performs a merge between two values of the same type.
|
||||
// viaPtr indicates whether the values were indirected through a pointer (implying proto2).
|
||||
// prop is set if this is a struct field (it may be nil).
|
||||
func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) {
|
||||
if in.Type() == protoMessageType {
|
||||
if !in.IsNil() {
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.ValueOf(Clone(in.Interface().(Message))))
|
||||
} else {
|
||||
Merge(out.Interface().(Message), in.Interface().(Message))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
switch in.Kind() {
|
||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
||||
reflect.String, reflect.Uint32, reflect.Uint64:
|
||||
if !viaPtr && isProto3Zero(in) {
|
||||
return
|
||||
}
|
||||
out.Set(in)
|
||||
case reflect.Interface:
|
||||
// Probably a oneof field; copy non-nil values.
|
||||
if in.IsNil() {
|
||||
return
|
||||
}
|
||||
// Allocate destination if it is not set, or set to a different type.
|
||||
// Otherwise we will merge as normal.
|
||||
if out.IsNil() || out.Elem().Type() != in.Elem().Type() {
|
||||
out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T)
|
||||
}
|
||||
mergeAny(out.Elem(), in.Elem(), false, nil)
|
||||
case reflect.Map:
|
||||
if in.Len() == 0 {
|
||||
return
|
||||
}
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.MakeMap(in.Type()))
|
||||
}
|
||||
// For maps with value types of *T or []byte we need to deep copy each value.
|
||||
elemKind := in.Type().Elem().Kind()
|
||||
for _, key := range in.MapKeys() {
|
||||
var val reflect.Value
|
||||
switch elemKind {
|
||||
case reflect.Ptr:
|
||||
val = reflect.New(in.Type().Elem().Elem())
|
||||
mergeAny(val, in.MapIndex(key), false, nil)
|
||||
case reflect.Slice:
|
||||
val = in.MapIndex(key)
|
||||
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
|
||||
default:
|
||||
val = in.MapIndex(key)
|
||||
}
|
||||
out.SetMapIndex(key, val)
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if in.IsNil() {
|
||||
return
|
||||
}
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.New(in.Elem().Type()))
|
||||
}
|
||||
mergeAny(out.Elem(), in.Elem(), true, nil)
|
||||
case reflect.Slice:
|
||||
if in.IsNil() {
|
||||
return
|
||||
}
|
||||
if in.Type().Elem().Kind() == reflect.Uint8 {
|
||||
// []byte is a scalar bytes field, not a repeated field.
|
||||
|
||||
// Edge case: if this is in a proto3 message, a zero length
|
||||
// bytes field is considered the zero value, and should not
|
||||
// be merged.
|
||||
if prop != nil && prop.proto3 && in.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Make a deep copy.
|
||||
// Append to []byte{} instead of []byte(nil) so that we never end up
|
||||
// with a nil result.
|
||||
out.SetBytes(append([]byte{}, in.Bytes()...))
|
||||
return
|
||||
}
|
||||
n := in.Len()
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.MakeSlice(in.Type(), 0, n))
|
||||
}
|
||||
switch in.Type().Elem().Kind() {
|
||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
||||
reflect.String, reflect.Uint32, reflect.Uint64:
|
||||
out.Set(reflect.AppendSlice(out, in))
|
||||
default:
|
||||
for i := 0; i < n; i++ {
|
||||
x := reflect.Indirect(reflect.New(in.Type().Elem()))
|
||||
mergeAny(x, in.Index(i), false, nil)
|
||||
out.Set(reflect.Append(out, x))
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
mergeStruct(out, in)
|
||||
default:
|
||||
// unknown type, so not a protocol buffer
|
||||
log.Printf("proto: don't know how to copy %v", in)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeExtension(out, in map[int32]Extension) {
|
||||
for extNum, eIn := range in {
|
||||
eOut := Extension{desc: eIn.desc}
|
||||
if eIn.value != nil {
|
||||
v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
|
||||
mergeAny(v, reflect.ValueOf(eIn.value), false, nil)
|
||||
eOut.value = v.Interface()
|
||||
}
|
||||
if eIn.enc != nil {
|
||||
eOut.enc = make([]byte, len(eIn.enc))
|
||||
copy(eOut.enc, eIn.enc)
|
||||
}
|
||||
|
||||
out[extNum] = eOut
|
||||
}
|
||||
}
|
868
Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go
generated
vendored
868
Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go
generated
vendored
@ -1,868 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
/*
|
||||
* Routines for decoding protocol buffer data to construct in-memory representations.
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// errOverflow is returned when an integer is too large to be represented.
|
||||
var errOverflow = errors.New("proto: integer overflow")
|
||||
|
||||
// ErrInternalBadWireType is returned by generated code when an incorrect
|
||||
// wire type is encountered. It does not get returned to user code.
|
||||
var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
|
||||
|
||||
// The fundamental decoders that interpret bytes on the wire.
|
||||
// Those that take integer types all return uint64 and are
|
||||
// therefore of type valueDecoder.
|
||||
|
||||
// DecodeVarint reads a varint-encoded integer from the slice.
|
||||
// It returns the integer and the number of bytes consumed, or
|
||||
// zero if there is not enough.
|
||||
// This is the format for the
|
||||
// int32, int64, uint32, uint64, bool, and enum
|
||||
// protocol buffer types.
|
||||
func DecodeVarint(buf []byte) (x uint64, n int) {
|
||||
// x, n already 0
|
||||
for shift := uint(0); shift < 64; shift += 7 {
|
||||
if n >= len(buf) {
|
||||
return 0, 0
|
||||
}
|
||||
b := uint64(buf[n])
|
||||
n++
|
||||
x |= (b & 0x7F) << shift
|
||||
if (b & 0x80) == 0 {
|
||||
return x, n
|
||||
}
|
||||
}
|
||||
|
||||
// The number is too large to represent in a 64-bit value.
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// DecodeVarint reads a varint-encoded integer from the Buffer.
|
||||
// This is the format for the
|
||||
// int32, int64, uint32, uint64, bool, and enum
|
||||
// protocol buffer types.
|
||||
func (p *Buffer) DecodeVarint() (x uint64, err error) {
|
||||
// x, err already 0
|
||||
|
||||
i := p.index
|
||||
l := len(p.buf)
|
||||
|
||||
for shift := uint(0); shift < 64; shift += 7 {
|
||||
if i >= l {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
b := p.buf[i]
|
||||
i++
|
||||
x |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
p.index = i
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// The number is too large to represent in a 64-bit value.
|
||||
err = errOverflow
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeFixed64 reads a 64-bit integer from the Buffer.
|
||||
// This is the format for the
|
||||
// fixed64, sfixed64, and double protocol buffer types.
|
||||
func (p *Buffer) DecodeFixed64() (x uint64, err error) {
|
||||
// x, err already 0
|
||||
i := p.index + 8
|
||||
if i < 0 || i > len(p.buf) {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
p.index = i
|
||||
|
||||
x = uint64(p.buf[i-8])
|
||||
x |= uint64(p.buf[i-7]) << 8
|
||||
x |= uint64(p.buf[i-6]) << 16
|
||||
x |= uint64(p.buf[i-5]) << 24
|
||||
x |= uint64(p.buf[i-4]) << 32
|
||||
x |= uint64(p.buf[i-3]) << 40
|
||||
x |= uint64(p.buf[i-2]) << 48
|
||||
x |= uint64(p.buf[i-1]) << 56
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeFixed32 reads a 32-bit integer from the Buffer.
|
||||
// This is the format for the
|
||||
// fixed32, sfixed32, and float protocol buffer types.
|
||||
func (p *Buffer) DecodeFixed32() (x uint64, err error) {
|
||||
// x, err already 0
|
||||
i := p.index + 4
|
||||
if i < 0 || i > len(p.buf) {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
p.index = i
|
||||
|
||||
x = uint64(p.buf[i-4])
|
||||
x |= uint64(p.buf[i-3]) << 8
|
||||
x |= uint64(p.buf[i-2]) << 16
|
||||
x |= uint64(p.buf[i-1]) << 24
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeZigzag64 reads a zigzag-encoded 64-bit integer
|
||||
// from the Buffer.
|
||||
// This is the format used for the sint64 protocol buffer type.
|
||||
func (p *Buffer) DecodeZigzag64() (x uint64, err error) {
|
||||
x, err = p.DecodeVarint()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63)
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeZigzag32 reads a zigzag-encoded 32-bit integer
|
||||
// from the Buffer.
|
||||
// This is the format used for the sint32 protocol buffer type.
|
||||
func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
|
||||
x, err = p.DecodeVarint()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31))
|
||||
return
|
||||
}
|
||||
|
||||
// These are not ValueDecoders: they produce an array of bytes or a string.
|
||||
// bytes, embedded messages
|
||||
|
||||
// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
|
||||
// This is the format used for the bytes protocol buffer
|
||||
// type and for embedded messages.
|
||||
func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) {
|
||||
n, err := p.DecodeVarint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nb := int(n)
|
||||
if nb < 0 {
|
||||
return nil, fmt.Errorf("proto: bad byte length %d", nb)
|
||||
}
|
||||
end := p.index + nb
|
||||
if end < p.index || end > len(p.buf) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
if !alloc {
|
||||
// todo: check if can get more uses of alloc=false
|
||||
buf = p.buf[p.index:end]
|
||||
p.index += nb
|
||||
return
|
||||
}
|
||||
|
||||
buf = make([]byte, nb)
|
||||
copy(buf, p.buf[p.index:])
|
||||
p.index += nb
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeStringBytes reads an encoded string from the Buffer.
|
||||
// This is the format used for the proto2 string type.
|
||||
func (p *Buffer) DecodeStringBytes() (s string, err error) {
|
||||
buf, err := p.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
|
||||
// If the protocol buffer has extensions, and the field matches, add it as an extension.
|
||||
// Otherwise, if the XXX_unrecognized field exists, append the skipped data there.
|
||||
func (o *Buffer) skipAndSave(t reflect.Type, tag, wire int, base structPointer, unrecField field) error {
|
||||
oi := o.index
|
||||
|
||||
err := o.skip(t, tag, wire)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !unrecField.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
ptr := structPointer_Bytes(base, unrecField)
|
||||
|
||||
// Add the skipped field to struct field
|
||||
obuf := o.buf
|
||||
|
||||
o.buf = *ptr
|
||||
o.EncodeVarint(uint64(tag<<3 | wire))
|
||||
*ptr = append(o.buf, obuf[oi:o.index]...)
|
||||
|
||||
o.buf = obuf
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
|
||||
func (o *Buffer) skip(t reflect.Type, tag, wire int) error {
|
||||
|
||||
var u uint64
|
||||
var err error
|
||||
|
||||
switch wire {
|
||||
case WireVarint:
|
||||
_, err = o.DecodeVarint()
|
||||
case WireFixed64:
|
||||
_, err = o.DecodeFixed64()
|
||||
case WireBytes:
|
||||
_, err = o.DecodeRawBytes(false)
|
||||
case WireFixed32:
|
||||
_, err = o.DecodeFixed32()
|
||||
case WireStartGroup:
|
||||
for {
|
||||
u, err = o.DecodeVarint()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
fwire := int(u & 0x7)
|
||||
if fwire == WireEndGroup {
|
||||
break
|
||||
}
|
||||
ftag := int(u >> 3)
|
||||
err = o.skip(t, ftag, fwire)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("proto: can't skip unknown wire type %d for %s", wire, t)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmarshaler is the interface representing objects that can
|
||||
// unmarshal themselves. The method should reset the receiver before
|
||||
// decoding starts. The argument points to data that may be
|
||||
// overwritten, so implementations should not keep references to the
|
||||
// buffer.
|
||||
type Unmarshaler interface {
|
||||
Unmarshal([]byte) error
|
||||
}
|
||||
|
||||
// Unmarshal parses the protocol buffer representation in buf and places the
|
||||
// decoded result in pb. If the struct underlying pb does not match
|
||||
// the data in buf, the results can be unpredictable.
|
||||
//
|
||||
// Unmarshal resets pb before starting to unmarshal, so any
|
||||
// existing data in pb is always removed. Use UnmarshalMerge
|
||||
// to preserve and append to existing data.
|
||||
func Unmarshal(buf []byte, pb Message) error {
|
||||
pb.Reset()
|
||||
return UnmarshalMerge(buf, pb)
|
||||
}
|
||||
|
||||
// UnmarshalMerge parses the protocol buffer representation in buf and
|
||||
// writes the decoded result to pb. If the struct underlying pb does not match
|
||||
// the data in buf, the results can be unpredictable.
|
||||
//
|
||||
// UnmarshalMerge merges into existing data in pb.
|
||||
// Most code should use Unmarshal instead.
|
||||
func UnmarshalMerge(buf []byte, pb Message) error {
|
||||
// If the object can unmarshal itself, let it.
|
||||
if u, ok := pb.(Unmarshaler); ok {
|
||||
return u.Unmarshal(buf)
|
||||
}
|
||||
return NewBuffer(buf).Unmarshal(pb)
|
||||
}
|
||||
|
||||
// DecodeMessage reads a count-delimited message from the Buffer.
|
||||
func (p *Buffer) DecodeMessage(pb Message) error {
|
||||
enc, err := p.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return NewBuffer(enc).Unmarshal(pb)
|
||||
}
|
||||
|
||||
// DecodeGroup reads a tag-delimited group from the Buffer.
|
||||
func (p *Buffer) DecodeGroup(pb Message) error {
|
||||
typ, base, err := getbase(pb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), true, base)
|
||||
}
|
||||
|
||||
// Unmarshal parses the protocol buffer representation in the
|
||||
// Buffer and places the decoded result in pb. If the struct
|
||||
// underlying pb does not match the data in the buffer, the results can be
|
||||
// unpredictable.
|
||||
func (p *Buffer) Unmarshal(pb Message) error {
|
||||
// If the object can unmarshal itself, let it.
|
||||
if u, ok := pb.(Unmarshaler); ok {
|
||||
err := u.Unmarshal(p.buf[p.index:])
|
||||
p.index = len(p.buf)
|
||||
return err
|
||||
}
|
||||
|
||||
typ, base, err := getbase(pb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), false, base)
|
||||
|
||||
if collectStats {
|
||||
stats.Decode++
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// unmarshalType does the work of unmarshaling a structure.
|
||||
func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group bool, base structPointer) error {
|
||||
var state errorState
|
||||
required, reqFields := prop.reqCount, uint64(0)
|
||||
|
||||
var err error
|
||||
for err == nil && o.index < len(o.buf) {
|
||||
oi := o.index
|
||||
var u uint64
|
||||
u, err = o.DecodeVarint()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
wire := int(u & 0x7)
|
||||
if wire == WireEndGroup {
|
||||
if is_group {
|
||||
return nil // input is satisfied
|
||||
}
|
||||
return fmt.Errorf("proto: %s: wiretype end group for non-group", st)
|
||||
}
|
||||
tag := int(u >> 3)
|
||||
if tag <= 0 {
|
||||
return fmt.Errorf("proto: %s: illegal tag %d (wire type %d)", st, tag, wire)
|
||||
}
|
||||
fieldnum, ok := prop.decoderTags.get(tag)
|
||||
if !ok {
|
||||
// Maybe it's an extension?
|
||||
if prop.extendable {
|
||||
if e := structPointer_Interface(base, st).(extendableProto); isExtensionField(e, int32(tag)) {
|
||||
if err = o.skip(st, tag, wire); err == nil {
|
||||
ext := e.ExtensionMap()[int32(tag)] // may be missing
|
||||
ext.enc = append(ext.enc, o.buf[oi:o.index]...)
|
||||
e.ExtensionMap()[int32(tag)] = ext
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Maybe it's a oneof?
|
||||
if prop.oneofUnmarshaler != nil {
|
||||
m := structPointer_Interface(base, st).(Message)
|
||||
// First return value indicates whether tag is a oneof field.
|
||||
ok, err = prop.oneofUnmarshaler(m, tag, wire, o)
|
||||
if err == ErrInternalBadWireType {
|
||||
// Map the error to something more descriptive.
|
||||
// Do the formatting here to save generated code space.
|
||||
err = fmt.Errorf("bad wiretype for oneof field in %T", m)
|
||||
}
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
err = o.skipAndSave(st, tag, wire, base, prop.unrecField)
|
||||
continue
|
||||
}
|
||||
p := prop.Prop[fieldnum]
|
||||
|
||||
if p.dec == nil {
|
||||
fmt.Fprintf(os.Stderr, "proto: no protobuf decoder for %s.%s\n", st, st.Field(fieldnum).Name)
|
||||
continue
|
||||
}
|
||||
dec := p.dec
|
||||
if wire != WireStartGroup && wire != p.WireType {
|
||||
if wire == WireBytes && p.packedDec != nil {
|
||||
// a packable field
|
||||
dec = p.packedDec
|
||||
} else {
|
||||
err = fmt.Errorf("proto: bad wiretype for field %s.%s: got wiretype %d, want %d", st, st.Field(fieldnum).Name, wire, p.WireType)
|
||||
continue
|
||||
}
|
||||
}
|
||||
decErr := dec(o, p, base)
|
||||
if decErr != nil && !state.shouldContinue(decErr, p) {
|
||||
err = decErr
|
||||
}
|
||||
if err == nil && p.Required {
|
||||
// Successfully decoded a required field.
|
||||
if tag <= 64 {
|
||||
// use bitmap for fields 1-64 to catch field reuse.
|
||||
var mask uint64 = 1 << uint64(tag-1)
|
||||
if reqFields&mask == 0 {
|
||||
// new required field
|
||||
reqFields |= mask
|
||||
required--
|
||||
}
|
||||
} else {
|
||||
// This is imprecise. It can be fooled by a required field
|
||||
// with a tag > 64 that is encoded twice; that's very rare.
|
||||
// A fully correct implementation would require allocating
|
||||
// a data structure, which we would like to avoid.
|
||||
required--
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if is_group {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if state.err != nil {
|
||||
return state.err
|
||||
}
|
||||
if required > 0 {
|
||||
// Not enough information to determine the exact field. If we use extra
|
||||
// CPU, we could determine the field only if the missing required field
|
||||
// has a tag <= 64 and we check reqFields.
|
||||
return &RequiredNotSetError{"{Unknown}"}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Individual type decoders
|
||||
// For each,
|
||||
// u is the decoded value,
|
||||
// v is a pointer to the field (pointer) in the struct
|
||||
|
||||
// Sizes of the pools to allocate inside the Buffer.
|
||||
// The goal is modest amortization and allocation
|
||||
// on at least 16-byte boundaries.
|
||||
const (
|
||||
boolPoolSize = 16
|
||||
uint32PoolSize = 8
|
||||
uint64PoolSize = 4
|
||||
)
|
||||
|
||||
// Decode a bool.
|
||||
func (o *Buffer) dec_bool(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(o.bools) == 0 {
|
||||
o.bools = make([]bool, boolPoolSize)
|
||||
}
|
||||
o.bools[0] = u != 0
|
||||
*structPointer_Bool(base, p.field) = &o.bools[0]
|
||||
o.bools = o.bools[1:]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Buffer) dec_proto3_bool(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*structPointer_BoolVal(base, p.field) = u != 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode an int32.
|
||||
func (o *Buffer) dec_int32(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
word32_Set(structPointer_Word32(base, p.field), o, uint32(u))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Buffer) dec_proto3_int32(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
word32Val_Set(structPointer_Word32Val(base, p.field), uint32(u))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode an int64.
|
||||
func (o *Buffer) dec_int64(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
word64_Set(structPointer_Word64(base, p.field), o, u)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Buffer) dec_proto3_int64(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
word64Val_Set(structPointer_Word64Val(base, p.field), o, u)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a string.
|
||||
func (o *Buffer) dec_string(p *Properties, base structPointer) error {
|
||||
s, err := o.DecodeStringBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*structPointer_String(base, p.field) = &s
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Buffer) dec_proto3_string(p *Properties, base structPointer) error {
|
||||
s, err := o.DecodeStringBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*structPointer_StringVal(base, p.field) = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of bytes ([]byte).
|
||||
func (o *Buffer) dec_slice_byte(p *Properties, base structPointer) error {
|
||||
b, err := o.DecodeRawBytes(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*structPointer_Bytes(base, p.field) = b
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of bools ([]bool).
|
||||
func (o *Buffer) dec_slice_bool(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := structPointer_BoolSlice(base, p.field)
|
||||
*v = append(*v, u != 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of bools ([]bool) in packed format.
|
||||
func (o *Buffer) dec_slice_packed_bool(p *Properties, base structPointer) error {
|
||||
v := structPointer_BoolSlice(base, p.field)
|
||||
|
||||
nn, err := o.DecodeVarint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nb := int(nn) // number of bytes of encoded bools
|
||||
fin := o.index + nb
|
||||
if fin < o.index {
|
||||
return errOverflow
|
||||
}
|
||||
|
||||
y := *v
|
||||
for o.index < fin {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
y = append(y, u != 0)
|
||||
}
|
||||
|
||||
*v = y
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of int32s ([]int32).
|
||||
func (o *Buffer) dec_slice_int32(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
structPointer_Word32Slice(base, p.field).Append(uint32(u))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of int32s ([]int32) in packed format.
|
||||
func (o *Buffer) dec_slice_packed_int32(p *Properties, base structPointer) error {
|
||||
v := structPointer_Word32Slice(base, p.field)
|
||||
|
||||
nn, err := o.DecodeVarint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nb := int(nn) // number of bytes of encoded int32s
|
||||
|
||||
fin := o.index + nb
|
||||
if fin < o.index {
|
||||
return errOverflow
|
||||
}
|
||||
for o.index < fin {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Append(uint32(u))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of int64s ([]int64).
|
||||
func (o *Buffer) dec_slice_int64(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
structPointer_Word64Slice(base, p.field).Append(u)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of int64s ([]int64) in packed format.
|
||||
func (o *Buffer) dec_slice_packed_int64(p *Properties, base structPointer) error {
|
||||
v := structPointer_Word64Slice(base, p.field)
|
||||
|
||||
nn, err := o.DecodeVarint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nb := int(nn) // number of bytes of encoded int64s
|
||||
|
||||
fin := o.index + nb
|
||||
if fin < o.index {
|
||||
return errOverflow
|
||||
}
|
||||
for o.index < fin {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Append(u)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of strings ([]string).
|
||||
func (o *Buffer) dec_slice_string(p *Properties, base structPointer) error {
|
||||
s, err := o.DecodeStringBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := structPointer_StringSlice(base, p.field)
|
||||
*v = append(*v, s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of slice of bytes ([][]byte).
|
||||
func (o *Buffer) dec_slice_slice_byte(p *Properties, base structPointer) error {
|
||||
b, err := o.DecodeRawBytes(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := structPointer_BytesSlice(base, p.field)
|
||||
*v = append(*v, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a map field.
|
||||
func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
|
||||
raw, err := o.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oi := o.index // index at the end of this map entry
|
||||
o.index -= len(raw) // move buffer back to start of map entry
|
||||
|
||||
mptr := structPointer_NewAt(base, p.field, p.mtype) // *map[K]V
|
||||
if mptr.Elem().IsNil() {
|
||||
mptr.Elem().Set(reflect.MakeMap(mptr.Type().Elem()))
|
||||
}
|
||||
v := mptr.Elem() // map[K]V
|
||||
|
||||
// Prepare addressable doubly-indirect placeholders for the key and value types.
|
||||
// See enc_new_map for why.
|
||||
keyptr := reflect.New(reflect.PtrTo(p.mtype.Key())).Elem() // addressable *K
|
||||
keybase := toStructPointer(keyptr.Addr()) // **K
|
||||
|
||||
var valbase structPointer
|
||||
var valptr reflect.Value
|
||||
switch p.mtype.Elem().Kind() {
|
||||
case reflect.Slice:
|
||||
// []byte
|
||||
var dummy []byte
|
||||
valptr = reflect.ValueOf(&dummy) // *[]byte
|
||||
valbase = toStructPointer(valptr) // *[]byte
|
||||
case reflect.Ptr:
|
||||
// message; valptr is **Msg; need to allocate the intermediate pointer
|
||||
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
|
||||
valptr.Set(reflect.New(valptr.Type().Elem()))
|
||||
valbase = toStructPointer(valptr)
|
||||
default:
|
||||
// everything else
|
||||
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
|
||||
valbase = toStructPointer(valptr.Addr()) // **V
|
||||
}
|
||||
|
||||
// Decode.
|
||||
// This parses a restricted wire format, namely the encoding of a message
|
||||
// with two fields. See enc_new_map for the format.
|
||||
for o.index < oi {
|
||||
// tagcode for key and value properties are always a single byte
|
||||
// because they have tags 1 and 2.
|
||||
tagcode := o.buf[o.index]
|
||||
o.index++
|
||||
switch tagcode {
|
||||
case p.mkeyprop.tagcode[0]:
|
||||
if err := p.mkeyprop.dec(o, p.mkeyprop, keybase); err != nil {
|
||||
return err
|
||||
}
|
||||
case p.mvalprop.tagcode[0]:
|
||||
if err := p.mvalprop.dec(o, p.mvalprop, valbase); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
// TODO: Should we silently skip this instead?
|
||||
return fmt.Errorf("proto: bad map data tag %d", raw[0])
|
||||
}
|
||||
}
|
||||
keyelem, valelem := keyptr.Elem(), valptr.Elem()
|
||||
if !keyelem.IsValid() {
|
||||
keyelem = reflect.Zero(p.mtype.Key())
|
||||
}
|
||||
if !valelem.IsValid() {
|
||||
valelem = reflect.Zero(p.mtype.Elem())
|
||||
}
|
||||
|
||||
v.SetMapIndex(keyelem, valelem)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a group.
|
||||
func (o *Buffer) dec_struct_group(p *Properties, base structPointer) error {
|
||||
bas := structPointer_GetStructPointer(base, p.field)
|
||||
if structPointer_IsNil(bas) {
|
||||
// allocate new nested message
|
||||
bas = toStructPointer(reflect.New(p.stype))
|
||||
structPointer_SetStructPointer(base, p.field, bas)
|
||||
}
|
||||
return o.unmarshalType(p.stype, p.sprop, true, bas)
|
||||
}
|
||||
|
||||
// Decode an embedded message.
|
||||
func (o *Buffer) dec_struct_message(p *Properties, base structPointer) (err error) {
|
||||
raw, e := o.DecodeRawBytes(false)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
bas := structPointer_GetStructPointer(base, p.field)
|
||||
if structPointer_IsNil(bas) {
|
||||
// allocate new nested message
|
||||
bas = toStructPointer(reflect.New(p.stype))
|
||||
structPointer_SetStructPointer(base, p.field, bas)
|
||||
}
|
||||
|
||||
// If the object can unmarshal itself, let it.
|
||||
if p.isUnmarshaler {
|
||||
iv := structPointer_Interface(bas, p.stype)
|
||||
return iv.(Unmarshaler).Unmarshal(raw)
|
||||
}
|
||||
|
||||
obuf := o.buf
|
||||
oi := o.index
|
||||
o.buf = raw
|
||||
o.index = 0
|
||||
|
||||
err = o.unmarshalType(p.stype, p.sprop, false, bas)
|
||||
o.buf = obuf
|
||||
o.index = oi
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode a slice of embedded messages.
|
||||
func (o *Buffer) dec_slice_struct_message(p *Properties, base structPointer) error {
|
||||
return o.dec_slice_struct(p, false, base)
|
||||
}
|
||||
|
||||
// Decode a slice of embedded groups.
|
||||
func (o *Buffer) dec_slice_struct_group(p *Properties, base structPointer) error {
|
||||
return o.dec_slice_struct(p, true, base)
|
||||
}
|
||||
|
||||
// Decode a slice of structs ([]*struct).
|
||||
func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base structPointer) error {
|
||||
v := reflect.New(p.stype)
|
||||
bas := toStructPointer(v)
|
||||
structPointer_StructPointerSlice(base, p.field).Append(bas)
|
||||
|
||||
if is_group {
|
||||
err := o.unmarshalType(p.stype, p.sprop, is_group, bas)
|
||||
return err
|
||||
}
|
||||
|
||||
raw, err := o.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the object can unmarshal itself, let it.
|
||||
if p.isUnmarshaler {
|
||||
iv := v.Interface()
|
||||
return iv.(Unmarshaler).Unmarshal(raw)
|
||||
}
|
||||
|
||||
obuf := o.buf
|
||||
oi := o.index
|
||||
o.buf = raw
|
||||
o.index = 0
|
||||
|
||||
err = o.unmarshalType(p.stype, p.sprop, is_group, bas)
|
||||
|
||||
o.buf = obuf
|
||||
o.index = oi
|
||||
|
||||
return err
|
||||
}
|
1331
Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go
generated
vendored
1331
Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go
generated
vendored
File diff suppressed because it is too large
Load Diff
276
Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go
generated
vendored
276
Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go
generated
vendored
@ -1,276 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Protocol buffer comparison.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
Equal returns true iff protocol buffers a and b are equal.
|
||||
The arguments must both be pointers to protocol buffer structs.
|
||||
|
||||
Equality is defined in this way:
|
||||
- Two messages are equal iff they are the same type,
|
||||
corresponding fields are equal, unknown field sets
|
||||
are equal, and extensions sets are equal.
|
||||
- Two set scalar fields are equal iff their values are equal.
|
||||
If the fields are of a floating-point type, remember that
|
||||
NaN != x for all x, including NaN. If the message is defined
|
||||
in a proto3 .proto file, fields are not "set"; specifically,
|
||||
zero length proto3 "bytes" fields are equal (nil == {}).
|
||||
- Two repeated fields are equal iff their lengths are the same,
|
||||
and their corresponding elements are equal (a "bytes" field,
|
||||
although represented by []byte, is not a repeated field)
|
||||
- Two unset fields are equal.
|
||||
- Two unknown field sets are equal if their current
|
||||
encoded state is equal.
|
||||
- Two extension sets are equal iff they have corresponding
|
||||
elements that are pairwise equal.
|
||||
- Every other combination of things are not equal.
|
||||
|
||||
The return value is undefined if a and b are not protocol buffers.
|
||||
*/
|
||||
func Equal(a, b Message) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == b
|
||||
}
|
||||
v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b)
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
if v1.Kind() == reflect.Ptr {
|
||||
if v1.IsNil() {
|
||||
return v2.IsNil()
|
||||
}
|
||||
if v2.IsNil() {
|
||||
return false
|
||||
}
|
||||
v1, v2 = v1.Elem(), v2.Elem()
|
||||
}
|
||||
if v1.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
return equalStruct(v1, v2)
|
||||
}
|
||||
|
||||
// v1 and v2 are known to have the same type.
|
||||
func equalStruct(v1, v2 reflect.Value) bool {
|
||||
sprop := GetProperties(v1.Type())
|
||||
for i := 0; i < v1.NumField(); i++ {
|
||||
f := v1.Type().Field(i)
|
||||
if strings.HasPrefix(f.Name, "XXX_") {
|
||||
continue
|
||||
}
|
||||
f1, f2 := v1.Field(i), v2.Field(i)
|
||||
if f.Type.Kind() == reflect.Ptr {
|
||||
if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 {
|
||||
// both unset
|
||||
continue
|
||||
} else if n1 != n2 {
|
||||
// set/unset mismatch
|
||||
return false
|
||||
}
|
||||
b1, ok := f1.Interface().(raw)
|
||||
if ok {
|
||||
b2 := f2.Interface().(raw)
|
||||
// RawMessage
|
||||
if !bytes.Equal(b1.Bytes(), b2.Bytes()) {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
f1, f2 = f1.Elem(), f2.Elem()
|
||||
}
|
||||
if !equalAny(f1, f2, sprop.Prop[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
|
||||
em2 := v2.FieldByName("XXX_extensions")
|
||||
if !equalExtensions(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
uf := v1.FieldByName("XXX_unrecognized")
|
||||
if !uf.IsValid() {
|
||||
return true
|
||||
}
|
||||
|
||||
u1 := uf.Bytes()
|
||||
u2 := v2.FieldByName("XXX_unrecognized").Bytes()
|
||||
if !bytes.Equal(u1, u2) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// v1 and v2 are known to have the same type.
|
||||
// prop may be nil.
|
||||
func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
|
||||
if v1.Type() == protoMessageType {
|
||||
m1, _ := v1.Interface().(Message)
|
||||
m2, _ := v2.Interface().(Message)
|
||||
return Equal(m1, m2)
|
||||
}
|
||||
switch v1.Kind() {
|
||||
case reflect.Bool:
|
||||
return v1.Bool() == v2.Bool()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v1.Float() == v2.Float()
|
||||
case reflect.Int32, reflect.Int64:
|
||||
return v1.Int() == v2.Int()
|
||||
case reflect.Interface:
|
||||
// Probably a oneof field; compare the inner values.
|
||||
n1, n2 := v1.IsNil(), v2.IsNil()
|
||||
if n1 || n2 {
|
||||
return n1 == n2
|
||||
}
|
||||
e1, e2 := v1.Elem(), v2.Elem()
|
||||
if e1.Type() != e2.Type() {
|
||||
return false
|
||||
}
|
||||
return equalAny(e1, e2, nil)
|
||||
case reflect.Map:
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
for _, key := range v1.MapKeys() {
|
||||
val2 := v2.MapIndex(key)
|
||||
if !val2.IsValid() {
|
||||
// This key was not found in the second map.
|
||||
return false
|
||||
}
|
||||
if !equalAny(v1.MapIndex(key), val2, nil) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Ptr:
|
||||
return equalAny(v1.Elem(), v2.Elem(), prop)
|
||||
case reflect.Slice:
|
||||
if v1.Type().Elem().Kind() == reflect.Uint8 {
|
||||
// short circuit: []byte
|
||||
|
||||
// Edge case: if this is in a proto3 message, a zero length
|
||||
// bytes field is considered the zero value.
|
||||
if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.IsNil() != v2.IsNil() {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte))
|
||||
}
|
||||
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !equalAny(v1.Index(i), v2.Index(i), prop) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.String:
|
||||
return v1.Interface().(string) == v2.Interface().(string)
|
||||
case reflect.Struct:
|
||||
return equalStruct(v1, v2)
|
||||
case reflect.Uint32, reflect.Uint64:
|
||||
return v1.Uint() == v2.Uint()
|
||||
}
|
||||
|
||||
// unknown type, so not a protocol buffer
|
||||
log.Printf("proto: don't know how to compare %v", v1)
|
||||
return false
|
||||
}
|
||||
|
||||
// base is the struct type that the extensions are based on.
|
||||
// em1 and em2 are extension maps.
|
||||
func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
||||
if len(em1) != len(em2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for extNum, e1 := range em1 {
|
||||
e2, ok := em2[extNum]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
m1, m2 := e1.value, e2.value
|
||||
|
||||
if m1 != nil && m2 != nil {
|
||||
// Both are unencoded.
|
||||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// At least one is encoded. To do a semantically correct comparison
|
||||
// we need to unmarshal them first.
|
||||
var desc *ExtensionDesc
|
||||
if m := extensionMaps[base]; m != nil {
|
||||
desc = m[extNum]
|
||||
}
|
||||
if desc == nil {
|
||||
log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
|
||||
continue
|
||||
}
|
||||
var err error
|
||||
if m1 == nil {
|
||||
m1, err = decodeExtension(e1.enc, desc)
|
||||
}
|
||||
if m2 == nil && err == nil {
|
||||
m2, err = decodeExtension(e2.enc, desc)
|
||||
}
|
||||
if err != nil {
|
||||
// The encoded form is invalid.
|
||||
log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
|
||||
return false
|
||||
}
|
||||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
399
Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go
generated
vendored
399
Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go
generated
vendored
@ -1,399 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
/*
|
||||
* Types and routines for supporting protocol buffer extensions.
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
|
||||
var ErrMissingExtension = errors.New("proto: missing extension")
|
||||
|
||||
// ExtensionRange represents a range of message extensions for a protocol buffer.
|
||||
// Used in code generated by the protocol compiler.
|
||||
type ExtensionRange struct {
|
||||
Start, End int32 // both inclusive
|
||||
}
|
||||
|
||||
// extendableProto is an interface implemented by any protocol buffer that may be extended.
|
||||
type extendableProto interface {
|
||||
Message
|
||||
ExtensionRangeArray() []ExtensionRange
|
||||
ExtensionMap() map[int32]Extension
|
||||
}
|
||||
|
||||
var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
|
||||
|
||||
// ExtensionDesc represents an extension specification.
|
||||
// Used in generated code from the protocol compiler.
|
||||
type ExtensionDesc struct {
|
||||
ExtendedType Message // nil pointer to the type that is being extended
|
||||
ExtensionType interface{} // nil pointer to the extension type
|
||||
Field int32 // field number
|
||||
Name string // fully-qualified name of extension, for text formatting
|
||||
Tag string // protobuf tag style
|
||||
}
|
||||
|
||||
func (ed *ExtensionDesc) repeated() bool {
|
||||
t := reflect.TypeOf(ed.ExtensionType)
|
||||
return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
|
||||
}
|
||||
|
||||
// Extension represents an extension in a message.
|
||||
type Extension struct {
|
||||
// When an extension is stored in a message using SetExtension
|
||||
// only desc and value are set. When the message is marshaled
|
||||
// enc will be set to the encoded form of the message.
|
||||
//
|
||||
// When a message is unmarshaled and contains extensions, each
|
||||
// extension will have only enc set. When such an extension is
|
||||
// accessed using GetExtension (or GetExtensions) desc and value
|
||||
// will be set.
|
||||
desc *ExtensionDesc
|
||||
value interface{}
|
||||
enc []byte
|
||||
}
|
||||
|
||||
// SetRawExtension is for testing only.
|
||||
func SetRawExtension(base extendableProto, id int32, b []byte) {
|
||||
base.ExtensionMap()[id] = Extension{enc: b}
|
||||
}
|
||||
|
||||
// isExtensionField returns true iff the given field number is in an extension range.
|
||||
func isExtensionField(pb extendableProto, field int32) bool {
|
||||
for _, er := range pb.ExtensionRangeArray() {
|
||||
if er.Start <= field && field <= er.End {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checkExtensionTypes checks that the given extension is valid for pb.
|
||||
func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
|
||||
// Check the extended type.
|
||||
if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b {
|
||||
return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String())
|
||||
}
|
||||
// Check the range.
|
||||
if !isExtensionField(pb, extension.Field) {
|
||||
return errors.New("proto: bad extension number; not in declared ranges")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extPropKey is sufficient to uniquely identify an extension.
|
||||
type extPropKey struct {
|
||||
base reflect.Type
|
||||
field int32
|
||||
}
|
||||
|
||||
var extProp = struct {
|
||||
sync.RWMutex
|
||||
m map[extPropKey]*Properties
|
||||
}{
|
||||
m: make(map[extPropKey]*Properties),
|
||||
}
|
||||
|
||||
func extensionProperties(ed *ExtensionDesc) *Properties {
|
||||
key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field}
|
||||
|
||||
extProp.RLock()
|
||||
if prop, ok := extProp.m[key]; ok {
|
||||
extProp.RUnlock()
|
||||
return prop
|
||||
}
|
||||
extProp.RUnlock()
|
||||
|
||||
extProp.Lock()
|
||||
defer extProp.Unlock()
|
||||
// Check again.
|
||||
if prop, ok := extProp.m[key]; ok {
|
||||
return prop
|
||||
}
|
||||
|
||||
prop := new(Properties)
|
||||
prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil)
|
||||
extProp.m[key] = prop
|
||||
return prop
|
||||
}
|
||||
|
||||
// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m.
|
||||
func encodeExtensionMap(m map[int32]Extension) error {
|
||||
for k, e := range m {
|
||||
if e.value == nil || e.desc == nil {
|
||||
// Extension is only in its encoded form.
|
||||
continue
|
||||
}
|
||||
|
||||
// We don't skip extensions that have an encoded form set,
|
||||
// because the extension value may have been mutated after
|
||||
// the last time this function was called.
|
||||
|
||||
et := reflect.TypeOf(e.desc.ExtensionType)
|
||||
props := extensionProperties(e.desc)
|
||||
|
||||
p := NewBuffer(nil)
|
||||
// If e.value has type T, the encoder expects a *struct{ X T }.
|
||||
// Pass a *T with a zero field and hope it all works out.
|
||||
x := reflect.New(et)
|
||||
x.Elem().Set(reflect.ValueOf(e.value))
|
||||
if err := props.enc(p, props, toStructPointer(x)); err != nil {
|
||||
return err
|
||||
}
|
||||
e.enc = p.buf
|
||||
m[k] = e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sizeExtensionMap(m map[int32]Extension) (n int) {
|
||||
for _, e := range m {
|
||||
if e.value == nil || e.desc == nil {
|
||||
// Extension is only in its encoded form.
|
||||
n += len(e.enc)
|
||||
continue
|
||||
}
|
||||
|
||||
// We don't skip extensions that have an encoded form set,
|
||||
// because the extension value may have been mutated after
|
||||
// the last time this function was called.
|
||||
|
||||
et := reflect.TypeOf(e.desc.ExtensionType)
|
||||
props := extensionProperties(e.desc)
|
||||
|
||||
// If e.value has type T, the encoder expects a *struct{ X T }.
|
||||
// Pass a *T with a zero field and hope it all works out.
|
||||
x := reflect.New(et)
|
||||
x.Elem().Set(reflect.ValueOf(e.value))
|
||||
n += props.size(props, toStructPointer(x))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HasExtension returns whether the given extension is present in pb.
|
||||
func HasExtension(pb extendableProto, extension *ExtensionDesc) bool {
|
||||
// TODO: Check types, field numbers, etc.?
|
||||
_, ok := pb.ExtensionMap()[extension.Field]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ClearExtension removes the given extension from pb.
|
||||
func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
|
||||
// TODO: Check types, field numbers, etc.?
|
||||
delete(pb.ExtensionMap(), extension.Field)
|
||||
}
|
||||
|
||||
// GetExtension parses and returns the given extension of pb.
|
||||
// If the extension is not present and has no default value it returns ErrMissingExtension.
|
||||
func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) {
|
||||
if err := checkExtensionTypes(pb, extension); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
emap := pb.ExtensionMap()
|
||||
e, ok := emap[extension.Field]
|
||||
if !ok {
|
||||
// defaultExtensionValue returns the default value or
|
||||
// ErrMissingExtension if there is no default.
|
||||
return defaultExtensionValue(extension)
|
||||
}
|
||||
|
||||
if e.value != nil {
|
||||
// Already decoded. Check the descriptor, though.
|
||||
if e.desc != extension {
|
||||
// This shouldn't happen. If it does, it means that
|
||||
// GetExtension was called twice with two different
|
||||
// descriptors with the same field number.
|
||||
return nil, errors.New("proto: descriptor conflict")
|
||||
}
|
||||
return e.value, nil
|
||||
}
|
||||
|
||||
v, err := decodeExtension(e.enc, extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remember the decoded version and drop the encoded version.
|
||||
// That way it is safe to mutate what we return.
|
||||
e.value = v
|
||||
e.desc = extension
|
||||
e.enc = nil
|
||||
emap[extension.Field] = e
|
||||
return e.value, nil
|
||||
}
|
||||
|
||||
// defaultExtensionValue returns the default value for extension.
|
||||
// If no default for an extension is defined ErrMissingExtension is returned.
|
||||
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
|
||||
t := reflect.TypeOf(extension.ExtensionType)
|
||||
props := extensionProperties(extension)
|
||||
|
||||
sf, _, err := fieldDefault(t, props)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sf == nil || sf.value == nil {
|
||||
// There is no default value.
|
||||
return nil, ErrMissingExtension
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Ptr {
|
||||
// We do not need to return a Ptr, we can directly return sf.value.
|
||||
return sf.value, nil
|
||||
}
|
||||
|
||||
// We need to return an interface{} that is a pointer to sf.value.
|
||||
value := reflect.New(t).Elem()
|
||||
value.Set(reflect.New(value.Type().Elem()))
|
||||
if sf.kind == reflect.Int32 {
|
||||
// We may have an int32 or an enum, but the underlying data is int32.
|
||||
// Since we can't set an int32 into a non int32 reflect.value directly
|
||||
// set it as a int32.
|
||||
value.Elem().SetInt(int64(sf.value.(int32)))
|
||||
} else {
|
||||
value.Elem().Set(reflect.ValueOf(sf.value))
|
||||
}
|
||||
return value.Interface(), nil
|
||||
}
|
||||
|
||||
// decodeExtension decodes an extension encoded in b.
|
||||
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
|
||||
o := NewBuffer(b)
|
||||
|
||||
t := reflect.TypeOf(extension.ExtensionType)
|
||||
|
||||
props := extensionProperties(extension)
|
||||
|
||||
// t is a pointer to a struct, pointer to basic type or a slice.
|
||||
// Allocate a "field" to store the pointer/slice itself; the
|
||||
// pointer/slice will be stored here. We pass
|
||||
// the address of this field to props.dec.
|
||||
// This passes a zero field and a *t and lets props.dec
|
||||
// interpret it as a *struct{ x t }.
|
||||
value := reflect.New(t).Elem()
|
||||
|
||||
for {
|
||||
// Discard wire type and field number varint. It isn't needed.
|
||||
if _, err := o.DecodeVarint(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := props.dec(o, props, toStructPointer(value.Addr())); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if o.index >= len(o.buf) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return value.Interface(), nil
|
||||
}
|
||||
|
||||
// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
|
||||
// The returned slice has the same length as es; missing extensions will appear as nil elements.
|
||||
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
|
||||
epb, ok := pb.(extendableProto)
|
||||
if !ok {
|
||||
err = errors.New("proto: not an extendable proto")
|
||||
return
|
||||
}
|
||||
extensions = make([]interface{}, len(es))
|
||||
for i, e := range es {
|
||||
extensions[i], err = GetExtension(epb, e)
|
||||
if err == ErrMissingExtension {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetExtension sets the specified extension of pb to the specified value.
|
||||
func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) error {
|
||||
if err := checkExtensionTypes(pb, extension); err != nil {
|
||||
return err
|
||||
}
|
||||
typ := reflect.TypeOf(extension.ExtensionType)
|
||||
if typ != reflect.TypeOf(value) {
|
||||
return errors.New("proto: bad extension value type")
|
||||
}
|
||||
// nil extension values need to be caught early, because the
|
||||
// encoder can't distinguish an ErrNil due to a nil extension
|
||||
// from an ErrNil due to a missing field. Extensions are
|
||||
// always optional, so the encoder would just swallow the error
|
||||
// and drop all the extensions from the encoded message.
|
||||
if reflect.ValueOf(value).IsNil() {
|
||||
return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
|
||||
}
|
||||
|
||||
pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A global registry of extensions.
|
||||
// The generated code will register the generated descriptors by calling RegisterExtension.
|
||||
|
||||
var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
|
||||
|
||||
// RegisterExtension is called from the generated code.
|
||||
func RegisterExtension(desc *ExtensionDesc) {
|
||||
st := reflect.TypeOf(desc.ExtendedType).Elem()
|
||||
m := extensionMaps[st]
|
||||
if m == nil {
|
||||
m = make(map[int32]*ExtensionDesc)
|
||||
extensionMaps[st] = m
|
||||
}
|
||||
if _, ok := m[desc.Field]; ok {
|
||||
panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
|
||||
}
|
||||
m[desc.Field] = desc
|
||||
}
|
||||
|
||||
// RegisteredExtensions returns a map of the registered extensions of a
|
||||
// protocol buffer struct, indexed by the extension number.
|
||||
// The argument pb should be a nil pointer to the struct type.
|
||||
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
|
||||
return extensionMaps[reflect.TypeOf(pb).Elem()]
|
||||
}
|
894
Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go
generated
vendored
894
Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go
generated
vendored
@ -1,894 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
/*
|
||||
Package proto converts data structures to and from the wire format of
|
||||
protocol buffers. It works in concert with the Go source code generated
|
||||
for .proto files by the protocol compiler.
|
||||
|
||||
A summary of the properties of the protocol buffer interface
|
||||
for a protocol buffer variable v:
|
||||
|
||||
- Names are turned from camel_case to CamelCase for export.
|
||||
- There are no methods on v to set fields; just treat
|
||||
them as structure fields.
|
||||
- There are getters that return a field's value if set,
|
||||
and return the field's default value if unset.
|
||||
The getters work even if the receiver is a nil message.
|
||||
- The zero value for a struct is its correct initialization state.
|
||||
All desired fields must be set before marshaling.
|
||||
- A Reset() method will restore a protobuf struct to its zero state.
|
||||
- Non-repeated fields are pointers to the values; nil means unset.
|
||||
That is, optional or required field int32 f becomes F *int32.
|
||||
- Repeated fields are slices.
|
||||
- Helper functions are available to aid the setting of fields.
|
||||
msg.Foo = proto.String("hello") // set field
|
||||
- Constants are defined to hold the default values of all fields that
|
||||
have them. They have the form Default_StructName_FieldName.
|
||||
Because the getter methods handle defaulted values,
|
||||
direct use of these constants should be rare.
|
||||
- Enums are given type names and maps from names to values.
|
||||
Enum values are prefixed by the enclosing message's name, or by the
|
||||
enum's type name if it is a top-level enum. Enum types have a String
|
||||
method, and a Enum method to assist in message construction.
|
||||
- Nested messages, groups and enums have type names prefixed with the name of
|
||||
the surrounding message type.
|
||||
- Extensions are given descriptor names that start with E_,
|
||||
followed by an underscore-delimited list of the nested messages
|
||||
that contain it (if any) followed by the CamelCased name of the
|
||||
extension field itself. HasExtension, ClearExtension, GetExtension
|
||||
and SetExtension are functions for manipulating extensions.
|
||||
- Oneof field sets are given a single field in their message,
|
||||
with distinguished wrapper types for each possible field value.
|
||||
- Marshal and Unmarshal are functions to encode and decode the wire format.
|
||||
|
||||
When the .proto file specifies `syntax="proto3"`, there are some differences:
|
||||
|
||||
- Non-repeated fields of non-message type are values instead of pointers.
|
||||
- Getters are only generated for message and oneof fields.
|
||||
- Enum types do not get an Enum method.
|
||||
|
||||
The simplest way to describe this is to see an example.
|
||||
Given file test.proto, containing
|
||||
|
||||
package example;
|
||||
|
||||
enum FOO { X = 17; }
|
||||
|
||||
message Test {
|
||||
required string label = 1;
|
||||
optional int32 type = 2 [default=77];
|
||||
repeated int64 reps = 3;
|
||||
optional group OptionalGroup = 4 {
|
||||
required string RequiredField = 5;
|
||||
}
|
||||
oneof union {
|
||||
int32 number = 6;
|
||||
string name = 7;
|
||||
}
|
||||
}
|
||||
|
||||
The resulting file, test.pb.go, is:
|
||||
|
||||
package example
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import math "math"
|
||||
|
||||
type FOO int32
|
||||
const (
|
||||
FOO_X FOO = 17
|
||||
)
|
||||
var FOO_name = map[int32]string{
|
||||
17: "X",
|
||||
}
|
||||
var FOO_value = map[string]int32{
|
||||
"X": 17,
|
||||
}
|
||||
|
||||
func (x FOO) Enum() *FOO {
|
||||
p := new(FOO)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
func (x FOO) String() string {
|
||||
return proto.EnumName(FOO_name, int32(x))
|
||||
}
|
||||
func (x *FOO) UnmarshalJSON(data []byte) error {
|
||||
value, err := proto.UnmarshalJSONEnum(FOO_value, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*x = FOO(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
|
||||
Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"`
|
||||
Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"`
|
||||
Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
|
||||
// Types that are valid to be assigned to Union:
|
||||
// *Test_Number
|
||||
// *Test_Name
|
||||
Union isTest_Union `protobuf_oneof:"union"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
func (m *Test) Reset() { *m = Test{} }
|
||||
func (m *Test) String() string { return proto.CompactTextString(m) }
|
||||
func (*Test) ProtoMessage() {}
|
||||
|
||||
type isTest_Union interface {
|
||||
isTest_Union()
|
||||
}
|
||||
|
||||
type Test_Number struct {
|
||||
Number int32 `protobuf:"varint,6,opt,name=number"`
|
||||
}
|
||||
type Test_Name struct {
|
||||
Name string `protobuf:"bytes,7,opt,name=name"`
|
||||
}
|
||||
|
||||
func (*Test_Number) isTest_Union() {}
|
||||
func (*Test_Name) isTest_Union() {}
|
||||
|
||||
func (m *Test) GetUnion() isTest_Union {
|
||||
if m != nil {
|
||||
return m.Union
|
||||
}
|
||||
return nil
|
||||
}
|
||||
const Default_Test_Type int32 = 77
|
||||
|
||||
func (m *Test) GetLabel() string {
|
||||
if m != nil && m.Label != nil {
|
||||
return *m.Label
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Test) GetType() int32 {
|
||||
if m != nil && m.Type != nil {
|
||||
return *m.Type
|
||||
}
|
||||
return Default_Test_Type
|
||||
}
|
||||
|
||||
func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
|
||||
if m != nil {
|
||||
return m.Optionalgroup
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Test_OptionalGroup struct {
|
||||
RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
|
||||
}
|
||||
func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} }
|
||||
func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) }
|
||||
|
||||
func (m *Test_OptionalGroup) GetRequiredField() string {
|
||||
if m != nil && m.RequiredField != nil {
|
||||
return *m.RequiredField
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Test) GetNumber() int32 {
|
||||
if x, ok := m.GetUnion().(*Test_Number); ok {
|
||||
return x.Number
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Test) GetName() string {
|
||||
if x, ok := m.GetUnion().(*Test_Name); ok {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
|
||||
}
|
||||
|
||||
To create and play with a Test object:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
pb "./example.pb"
|
||||
)
|
||||
|
||||
func main() {
|
||||
test := &pb.Test{
|
||||
Label: proto.String("hello"),
|
||||
Type: proto.Int32(17),
|
||||
Reps: []int64{1, 2, 3},
|
||||
Optionalgroup: &pb.Test_OptionalGroup{
|
||||
RequiredField: proto.String("good bye"),
|
||||
},
|
||||
Union: &pb.Test_Name{"fred"},
|
||||
}
|
||||
data, err := proto.Marshal(test)
|
||||
if err != nil {
|
||||
log.Fatal("marshaling error: ", err)
|
||||
}
|
||||
newTest := &pb.Test{}
|
||||
err = proto.Unmarshal(data, newTest)
|
||||
if err != nil {
|
||||
log.Fatal("unmarshaling error: ", err)
|
||||
}
|
||||
// Now test and newTest contain the same data.
|
||||
if test.GetLabel() != newTest.GetLabel() {
|
||||
log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel())
|
||||
}
|
||||
// Use a type switch to determine which oneof was set.
|
||||
switch u := test.Union.(type) {
|
||||
case *pb.Test_Number: // u.Number contains the number.
|
||||
case *pb.Test_Name: // u.Name contains the string.
|
||||
}
|
||||
// etc.
|
||||
}
|
||||
*/
|
||||
package proto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Message is implemented by generated protocol buffer messages.
|
||||
type Message interface {
|
||||
Reset()
|
||||
String() string
|
||||
ProtoMessage()
|
||||
}
|
||||
|
||||
// Stats records allocation details about the protocol buffer encoders
|
||||
// and decoders. Useful for tuning the library itself.
|
||||
type Stats struct {
|
||||
Emalloc uint64 // mallocs in encode
|
||||
Dmalloc uint64 // mallocs in decode
|
||||
Encode uint64 // number of encodes
|
||||
Decode uint64 // number of decodes
|
||||
Chit uint64 // number of cache hits
|
||||
Cmiss uint64 // number of cache misses
|
||||
Size uint64 // number of sizes
|
||||
}
|
||||
|
||||
// Set to true to enable stats collection.
|
||||
const collectStats = false
|
||||
|
||||
var stats Stats
|
||||
|
||||
// GetStats returns a copy of the global Stats structure.
|
||||
func GetStats() Stats { return stats }
|
||||
|
||||
// A Buffer is a buffer manager for marshaling and unmarshaling
|
||||
// protocol buffers. It may be reused between invocations to
|
||||
// reduce memory usage. It is not necessary to use a Buffer;
|
||||
// the global functions Marshal and Unmarshal create a
|
||||
// temporary Buffer and are fine for most applications.
|
||||
type Buffer struct {
|
||||
buf []byte // encode/decode byte stream
|
||||
index int // write point
|
||||
|
||||
// pools of basic types to amortize allocation.
|
||||
bools []bool
|
||||
uint32s []uint32
|
||||
uint64s []uint64
|
||||
|
||||
// extra pools, only used with pointer_reflect.go
|
||||
int32s []int32
|
||||
int64s []int64
|
||||
float32s []float32
|
||||
float64s []float64
|
||||
}
|
||||
|
||||
// NewBuffer allocates a new Buffer and initializes its internal data to
|
||||
// the contents of the argument slice.
|
||||
func NewBuffer(e []byte) *Buffer {
|
||||
return &Buffer{buf: e}
|
||||
}
|
||||
|
||||
// Reset resets the Buffer, ready for marshaling a new protocol buffer.
|
||||
func (p *Buffer) Reset() {
|
||||
p.buf = p.buf[0:0] // for reading/writing
|
||||
p.index = 0 // for reading
|
||||
}
|
||||
|
||||
// SetBuf replaces the internal buffer with the slice,
|
||||
// ready for unmarshaling the contents of the slice.
|
||||
func (p *Buffer) SetBuf(s []byte) {
|
||||
p.buf = s
|
||||
p.index = 0
|
||||
}
|
||||
|
||||
// Bytes returns the contents of the Buffer.
|
||||
func (p *Buffer) Bytes() []byte { return p.buf }
|
||||
|
||||
/*
|
||||
* Helper routines for simplifying the creation of optional fields of basic type.
|
||||
*/
|
||||
|
||||
// Bool is a helper routine that allocates a new bool value
|
||||
// to store v and returns a pointer to it.
|
||||
func Bool(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Int32 is a helper routine that allocates a new int32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int32(v int32) *int32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Int is a helper routine that allocates a new int32 value
|
||||
// to store v and returns a pointer to it, but unlike Int32
|
||||
// its argument value is an int.
|
||||
func Int(v int) *int32 {
|
||||
p := new(int32)
|
||||
*p = int32(v)
|
||||
return p
|
||||
}
|
||||
|
||||
// Int64 is a helper routine that allocates a new int64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int64(v int64) *int64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Float32 is a helper routine that allocates a new float32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Float32(v float32) *float32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Float64 is a helper routine that allocates a new float64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Float64(v float64) *float64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Uint32 is a helper routine that allocates a new uint32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint32(v uint32) *uint32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Uint64 is a helper routine that allocates a new uint64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint64(v uint64) *uint64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// String is a helper routine that allocates a new string value
|
||||
// to store v and returns a pointer to it.
|
||||
func String(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
// EnumName is a helper function to simplify printing protocol buffer enums
|
||||
// by name. Given an enum map and a value, it returns a useful string.
|
||||
func EnumName(m map[int32]string, v int32) string {
|
||||
s, ok := m[v]
|
||||
if ok {
|
||||
return s
|
||||
}
|
||||
return strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
// UnmarshalJSONEnum is a helper function to simplify recovering enum int values
|
||||
// from their JSON-encoded representation. Given a map from the enum's symbolic
|
||||
// names to its int values, and a byte buffer containing the JSON-encoded
|
||||
// value, it returns an int32 that can be cast to the enum type by the caller.
|
||||
//
|
||||
// The function can deal with both JSON representations, numeric and symbolic.
|
||||
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
|
||||
if data[0] == '"' {
|
||||
// New style: enums are strings.
|
||||
var repr string
|
||||
if err := json.Unmarshal(data, &repr); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
val, ok := m[repr]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
// Old style: enums are ints.
|
||||
var val int32
|
||||
if err := json.Unmarshal(data, &val); err != nil {
|
||||
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// DebugPrint dumps the encoded data in b in a debugging format with a header
|
||||
// including the string s. Used in testing but made available for general debugging.
|
||||
func (p *Buffer) DebugPrint(s string, b []byte) {
|
||||
var u uint64
|
||||
|
||||
obuf := p.buf
|
||||
index := p.index
|
||||
p.buf = b
|
||||
p.index = 0
|
||||
depth := 0
|
||||
|
||||
fmt.Printf("\n--- %s ---\n", s)
|
||||
|
||||
out:
|
||||
for {
|
||||
for i := 0; i < depth; i++ {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
|
||||
index := p.index
|
||||
if index == len(p.buf) {
|
||||
break
|
||||
}
|
||||
|
||||
op, err := p.DecodeVarint()
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: fetching op err %v\n", index, err)
|
||||
break out
|
||||
}
|
||||
tag := op >> 3
|
||||
wire := op & 7
|
||||
|
||||
switch wire {
|
||||
default:
|
||||
fmt.Printf("%3d: t=%3d unknown wire=%d\n",
|
||||
index, tag, wire)
|
||||
break out
|
||||
|
||||
case WireBytes:
|
||||
var r []byte
|
||||
|
||||
r, err = p.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r))
|
||||
if len(r) <= 6 {
|
||||
for i := 0; i < len(r); i++ {
|
||||
fmt.Printf(" %.2x", r[i])
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < 3; i++ {
|
||||
fmt.Printf(" %.2x", r[i])
|
||||
}
|
||||
fmt.Printf(" ..")
|
||||
for i := len(r) - 3; i < len(r); i++ {
|
||||
fmt.Printf(" %.2x", r[i])
|
||||
}
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
|
||||
case WireFixed32:
|
||||
u, err = p.DecodeFixed32()
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err)
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u)
|
||||
|
||||
case WireFixed64:
|
||||
u, err = p.DecodeFixed64()
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err)
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u)
|
||||
|
||||
case WireVarint:
|
||||
u, err = p.DecodeVarint()
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err)
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u)
|
||||
|
||||
case WireStartGroup:
|
||||
fmt.Printf("%3d: t=%3d start\n", index, tag)
|
||||
depth++
|
||||
|
||||
case WireEndGroup:
|
||||
depth--
|
||||
fmt.Printf("%3d: t=%3d end\n", index, tag)
|
||||
}
|
||||
}
|
||||
|
||||
if depth != 0 {
|
||||
fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
|
||||
p.buf = obuf
|
||||
p.index = index
|
||||
}
|
||||
|
||||
// SetDefaults sets unset protocol buffer fields to their default values.
|
||||
// It only modifies fields that are both unset and have defined defaults.
|
||||
// It recursively sets default values in any non-nil sub-messages.
|
||||
func SetDefaults(pb Message) {
|
||||
setDefaults(reflect.ValueOf(pb), true, false)
|
||||
}
|
||||
|
||||
// v is a pointer to a struct.
|
||||
func setDefaults(v reflect.Value, recur, zeros bool) {
|
||||
v = v.Elem()
|
||||
|
||||
defaultMu.RLock()
|
||||
dm, ok := defaults[v.Type()]
|
||||
defaultMu.RUnlock()
|
||||
if !ok {
|
||||
dm = buildDefaultMessage(v.Type())
|
||||
defaultMu.Lock()
|
||||
defaults[v.Type()] = dm
|
||||
defaultMu.Unlock()
|
||||
}
|
||||
|
||||
for _, sf := range dm.scalars {
|
||||
f := v.Field(sf.index)
|
||||
if !f.IsNil() {
|
||||
// field already set
|
||||
continue
|
||||
}
|
||||
dv := sf.value
|
||||
if dv == nil && !zeros {
|
||||
// no explicit default, and don't want to set zeros
|
||||
continue
|
||||
}
|
||||
fptr := f.Addr().Interface() // **T
|
||||
// TODO: Consider batching the allocations we do here.
|
||||
switch sf.kind {
|
||||
case reflect.Bool:
|
||||
b := new(bool)
|
||||
if dv != nil {
|
||||
*b = dv.(bool)
|
||||
}
|
||||
*(fptr.(**bool)) = b
|
||||
case reflect.Float32:
|
||||
f := new(float32)
|
||||
if dv != nil {
|
||||
*f = dv.(float32)
|
||||
}
|
||||
*(fptr.(**float32)) = f
|
||||
case reflect.Float64:
|
||||
f := new(float64)
|
||||
if dv != nil {
|
||||
*f = dv.(float64)
|
||||
}
|
||||
*(fptr.(**float64)) = f
|
||||
case reflect.Int32:
|
||||
// might be an enum
|
||||
if ft := f.Type(); ft != int32PtrType {
|
||||
// enum
|
||||
f.Set(reflect.New(ft.Elem()))
|
||||
if dv != nil {
|
||||
f.Elem().SetInt(int64(dv.(int32)))
|
||||
}
|
||||
} else {
|
||||
// int32 field
|
||||
i := new(int32)
|
||||
if dv != nil {
|
||||
*i = dv.(int32)
|
||||
}
|
||||
*(fptr.(**int32)) = i
|
||||
}
|
||||
case reflect.Int64:
|
||||
i := new(int64)
|
||||
if dv != nil {
|
||||
*i = dv.(int64)
|
||||
}
|
||||
*(fptr.(**int64)) = i
|
||||
case reflect.String:
|
||||
s := new(string)
|
||||
if dv != nil {
|
||||
*s = dv.(string)
|
||||
}
|
||||
*(fptr.(**string)) = s
|
||||
case reflect.Uint8:
|
||||
// exceptional case: []byte
|
||||
var b []byte
|
||||
if dv != nil {
|
||||
db := dv.([]byte)
|
||||
b = make([]byte, len(db))
|
||||
copy(b, db)
|
||||
} else {
|
||||
b = []byte{}
|
||||
}
|
||||
*(fptr.(*[]byte)) = b
|
||||
case reflect.Uint32:
|
||||
u := new(uint32)
|
||||
if dv != nil {
|
||||
*u = dv.(uint32)
|
||||
}
|
||||
*(fptr.(**uint32)) = u
|
||||
case reflect.Uint64:
|
||||
u := new(uint64)
|
||||
if dv != nil {
|
||||
*u = dv.(uint64)
|
||||
}
|
||||
*(fptr.(**uint64)) = u
|
||||
default:
|
||||
log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ni := range dm.nested {
|
||||
f := v.Field(ni)
|
||||
// f is *T or []*T or map[T]*T
|
||||
switch f.Kind() {
|
||||
case reflect.Ptr:
|
||||
if f.IsNil() {
|
||||
continue
|
||||
}
|
||||
setDefaults(f, recur, zeros)
|
||||
|
||||
case reflect.Slice:
|
||||
for i := 0; i < f.Len(); i++ {
|
||||
e := f.Index(i)
|
||||
if e.IsNil() {
|
||||
continue
|
||||
}
|
||||
setDefaults(e, recur, zeros)
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
for _, k := range f.MapKeys() {
|
||||
e := f.MapIndex(k)
|
||||
if e.IsNil() {
|
||||
continue
|
||||
}
|
||||
setDefaults(e, recur, zeros)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// defaults maps a protocol buffer struct type to a slice of the fields,
|
||||
// with its scalar fields set to their proto-declared non-zero default values.
|
||||
defaultMu sync.RWMutex
|
||||
defaults = make(map[reflect.Type]defaultMessage)
|
||||
|
||||
int32PtrType = reflect.TypeOf((*int32)(nil))
|
||||
)
|
||||
|
||||
// defaultMessage represents information about the default values of a message.
|
||||
type defaultMessage struct {
|
||||
scalars []scalarField
|
||||
nested []int // struct field index of nested messages
|
||||
}
|
||||
|
||||
type scalarField struct {
|
||||
index int // struct field index
|
||||
kind reflect.Kind // element type (the T in *T or []T)
|
||||
value interface{} // the proto-declared default value, or nil
|
||||
}
|
||||
|
||||
// t is a struct type.
|
||||
func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
|
||||
sprop := GetProperties(t)
|
||||
for _, prop := range sprop.Prop {
|
||||
fi, ok := sprop.decoderTags.get(prop.Tag)
|
||||
if !ok {
|
||||
// XXX_unrecognized
|
||||
continue
|
||||
}
|
||||
ft := t.Field(fi).Type
|
||||
|
||||
sf, nested, err := fieldDefault(ft, prop)
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Print(err)
|
||||
case nested:
|
||||
dm.nested = append(dm.nested, fi)
|
||||
case sf != nil:
|
||||
sf.index = fi
|
||||
dm.scalars = append(dm.scalars, *sf)
|
||||
}
|
||||
}
|
||||
|
||||
return dm
|
||||
}
|
||||
|
||||
// fieldDefault returns the scalarField for field type ft.
|
||||
// sf will be nil if the field can not have a default.
|
||||
// nestedMessage will be true if this is a nested message.
|
||||
// Note that sf.index is not set on return.
|
||||
func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
|
||||
var canHaveDefault bool
|
||||
switch ft.Kind() {
|
||||
case reflect.Ptr:
|
||||
if ft.Elem().Kind() == reflect.Struct {
|
||||
nestedMessage = true
|
||||
} else {
|
||||
canHaveDefault = true // proto2 scalar field
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
switch ft.Elem().Kind() {
|
||||
case reflect.Ptr:
|
||||
nestedMessage = true // repeated message
|
||||
case reflect.Uint8:
|
||||
canHaveDefault = true // bytes field
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
if ft.Elem().Kind() == reflect.Ptr {
|
||||
nestedMessage = true // map with message values
|
||||
}
|
||||
}
|
||||
|
||||
if !canHaveDefault {
|
||||
if nestedMessage {
|
||||
return nil, true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// We now know that ft is a pointer or slice.
|
||||
sf = &scalarField{kind: ft.Elem().Kind()}
|
||||
|
||||
// scalar fields without defaults
|
||||
if !prop.HasDefault {
|
||||
return sf, false, nil
|
||||
}
|
||||
|
||||
// a scalar field: either *T or []byte
|
||||
switch ft.Elem().Kind() {
|
||||
case reflect.Bool:
|
||||
x, err := strconv.ParseBool(prop.Default)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = x
|
||||
case reflect.Float32:
|
||||
x, err := strconv.ParseFloat(prop.Default, 32)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = float32(x)
|
||||
case reflect.Float64:
|
||||
x, err := strconv.ParseFloat(prop.Default, 64)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = x
|
||||
case reflect.Int32:
|
||||
x, err := strconv.ParseInt(prop.Default, 10, 32)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = int32(x)
|
||||
case reflect.Int64:
|
||||
x, err := strconv.ParseInt(prop.Default, 10, 64)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = x
|
||||
case reflect.String:
|
||||
sf.value = prop.Default
|
||||
case reflect.Uint8:
|
||||
// []byte (not *uint8)
|
||||
sf.value = []byte(prop.Default)
|
||||
case reflect.Uint32:
|
||||
x, err := strconv.ParseUint(prop.Default, 10, 32)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = uint32(x)
|
||||
case reflect.Uint64:
|
||||
x, err := strconv.ParseUint(prop.Default, 10, 64)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = x
|
||||
default:
|
||||
return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
|
||||
}
|
||||
|
||||
return sf, false, nil
|
||||
}
|
||||
|
||||
// Map fields may have key types of non-float scalars, strings and enums.
|
||||
// The easiest way to sort them in some deterministic order is to use fmt.
|
||||
// If this turns out to be inefficient we can always consider other options,
|
||||
// such as doing a Schwartzian transform.
|
||||
|
||||
func mapKeys(vs []reflect.Value) sort.Interface {
|
||||
s := mapKeySorter{
|
||||
vs: vs,
|
||||
// default Less function: textual comparison
|
||||
less: func(a, b reflect.Value) bool {
|
||||
return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface())
|
||||
},
|
||||
}
|
||||
|
||||
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps;
|
||||
// numeric keys are sorted numerically.
|
||||
if len(vs) == 0 {
|
||||
return s
|
||||
}
|
||||
switch vs[0].Kind() {
|
||||
case reflect.Int32, reflect.Int64:
|
||||
s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
||||
case reflect.Uint32, reflect.Uint64:
|
||||
s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
type mapKeySorter struct {
|
||||
vs []reflect.Value
|
||||
less func(a, b reflect.Value) bool
|
||||
}
|
||||
|
||||
func (s mapKeySorter) Len() int { return len(s.vs) }
|
||||
func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
|
||||
func (s mapKeySorter) Less(i, j int) bool {
|
||||
return s.less(s.vs[i], s.vs[j])
|
||||
}
|
||||
|
||||
// isProto3Zero reports whether v is a zero proto3 value.
|
||||
func isProto3Zero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint32, reflect.Uint64:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.String:
|
||||
return v.String() == ""
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
||||
// to assert that that code is compatible with this version of the proto package.
|
||||
const ProtoPackageIsVersion1 = true
|
280
Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go
generated
vendored
280
Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go
generated
vendored
@ -1,280 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
/*
|
||||
* Support for message sets.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
|
||||
// A message type ID is required for storing a protocol buffer in a message set.
|
||||
var errNoMessageTypeID = errors.New("proto does not have a message type ID")
|
||||
|
||||
// The first two types (_MessageSet_Item and messageSet)
|
||||
// model what the protocol compiler produces for the following protocol message:
|
||||
// message MessageSet {
|
||||
// repeated group Item = 1 {
|
||||
// required int32 type_id = 2;
|
||||
// required string message = 3;
|
||||
// };
|
||||
// }
|
||||
// That is the MessageSet wire format. We can't use a proto to generate these
|
||||
// because that would introduce a circular dependency between it and this package.
|
||||
|
||||
type _MessageSet_Item struct {
|
||||
TypeId *int32 `protobuf:"varint,2,req,name=type_id"`
|
||||
Message []byte `protobuf:"bytes,3,req,name=message"`
|
||||
}
|
||||
|
||||
type messageSet struct {
|
||||
Item []*_MessageSet_Item `protobuf:"group,1,rep"`
|
||||
XXX_unrecognized []byte
|
||||
// TODO: caching?
|
||||
}
|
||||
|
||||
// Make sure messageSet is a Message.
|
||||
var _ Message = (*messageSet)(nil)
|
||||
|
||||
// messageTypeIder is an interface satisfied by a protocol buffer type
|
||||
// that may be stored in a MessageSet.
|
||||
type messageTypeIder interface {
|
||||
MessageTypeId() int32
|
||||
}
|
||||
|
||||
func (ms *messageSet) find(pb Message) *_MessageSet_Item {
|
||||
mti, ok := pb.(messageTypeIder)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
id := mti.MessageTypeId()
|
||||
for _, item := range ms.Item {
|
||||
if *item.TypeId == id {
|
||||
return item
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *messageSet) Has(pb Message) bool {
|
||||
if ms.find(pb) != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ms *messageSet) Unmarshal(pb Message) error {
|
||||
if item := ms.find(pb); item != nil {
|
||||
return Unmarshal(item.Message, pb)
|
||||
}
|
||||
if _, ok := pb.(messageTypeIder); !ok {
|
||||
return errNoMessageTypeID
|
||||
}
|
||||
return nil // TODO: return error instead?
|
||||
}
|
||||
|
||||
func (ms *messageSet) Marshal(pb Message) error {
|
||||
msg, err := Marshal(pb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if item := ms.find(pb); item != nil {
|
||||
// reuse existing item
|
||||
item.Message = msg
|
||||
return nil
|
||||
}
|
||||
|
||||
mti, ok := pb.(messageTypeIder)
|
||||
if !ok {
|
||||
return errNoMessageTypeID
|
||||
}
|
||||
|
||||
mtid := mti.MessageTypeId()
|
||||
ms.Item = append(ms.Item, &_MessageSet_Item{
|
||||
TypeId: &mtid,
|
||||
Message: msg,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *messageSet) Reset() { *ms = messageSet{} }
|
||||
func (ms *messageSet) String() string { return CompactTextString(ms) }
|
||||
func (*messageSet) ProtoMessage() {}
|
||||
|
||||
// Support for the message_set_wire_format message option.
|
||||
|
||||
func skipVarint(buf []byte) []byte {
|
||||
i := 0
|
||||
for ; buf[i]&0x80 != 0; i++ {
|
||||
}
|
||||
return buf[i+1:]
|
||||
}
|
||||
|
||||
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
|
||||
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func MarshalMessageSet(m map[int32]Extension) ([]byte, error) {
|
||||
if err := encodeExtensionMap(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sort extension IDs to provide a deterministic encoding.
|
||||
// See also enc_map in encode.go.
|
||||
ids := make([]int, 0, len(m))
|
||||
for id := range m {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
ms := &messageSet{Item: make([]*_MessageSet_Item, 0, len(m))}
|
||||
for _, id := range ids {
|
||||
e := m[int32(id)]
|
||||
// Remove the wire type and field number varint, as well as the length varint.
|
||||
msg := skipVarint(skipVarint(e.enc))
|
||||
|
||||
ms.Item = append(ms.Item, &_MessageSet_Item{
|
||||
TypeId: Int32(int32(id)),
|
||||
Message: msg,
|
||||
})
|
||||
}
|
||||
return Marshal(ms)
|
||||
}
|
||||
|
||||
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
||||
// It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func UnmarshalMessageSet(buf []byte, m map[int32]Extension) error {
|
||||
ms := new(messageSet)
|
||||
if err := Unmarshal(buf, ms); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range ms.Item {
|
||||
id := *item.TypeId
|
||||
msg := item.Message
|
||||
|
||||
// Restore wire type and field number varint, plus length varint.
|
||||
// Be careful to preserve duplicate items.
|
||||
b := EncodeVarint(uint64(id)<<3 | WireBytes)
|
||||
if ext, ok := m[id]; ok {
|
||||
// Existing data; rip off the tag and length varint
|
||||
// so we join the new data correctly.
|
||||
// We can assume that ext.enc is set because we are unmarshaling.
|
||||
o := ext.enc[len(b):] // skip wire type and field number
|
||||
_, n := DecodeVarint(o) // calculate length of length varint
|
||||
o = o[n:] // skip length varint
|
||||
msg = append(o, msg...) // join old data and new data
|
||||
}
|
||||
b = append(b, EncodeVarint(uint64(len(msg)))...)
|
||||
b = append(b, msg...)
|
||||
|
||||
m[id] = Extension{enc: b}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
|
||||
// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func MarshalMessageSetJSON(m map[int32]Extension) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
b.WriteByte('{')
|
||||
|
||||
// Process the map in key order for deterministic output.
|
||||
ids := make([]int32, 0, len(m))
|
||||
for id := range m {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
|
||||
|
||||
for i, id := range ids {
|
||||
ext := m[id]
|
||||
if i > 0 {
|
||||
b.WriteByte(',')
|
||||
}
|
||||
|
||||
msd, ok := messageSetMap[id]
|
||||
if !ok {
|
||||
// Unknown type; we can't render it, so skip it.
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&b, `"[%s]":`, msd.name)
|
||||
|
||||
x := ext.value
|
||||
if x == nil {
|
||||
x = reflect.New(msd.t.Elem()).Interface()
|
||||
if err := Unmarshal(ext.enc, x.(Message)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d, err := json.Marshal(x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Write(d)
|
||||
}
|
||||
b.WriteByte('}')
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
|
||||
// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func UnmarshalMessageSetJSON(buf []byte, m map[int32]Extension) error {
|
||||
// Common-case fast path.
|
||||
if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is fairly tricky, and it's not clear that it is needed.
|
||||
return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
|
||||
}
|
||||
|
||||
// A global registry of types that can be used in a MessageSet.
|
||||
|
||||
var messageSetMap = make(map[int32]messageSetDesc)
|
||||
|
||||
type messageSetDesc struct {
|
||||
t reflect.Type // pointer to struct
|
||||
name string
|
||||
}
|
||||
|
||||
// RegisterMessageSetType is called from the generated code.
|
||||
func RegisterMessageSetType(m Message, fieldNum int32, name string) {
|
||||
messageSetMap[fieldNum] = messageSetDesc{
|
||||
t: reflect.TypeOf(m),
|
||||
name: name,
|
||||
}
|
||||
}
|
479
Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
479
Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
@ -1,479 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// +build appengine js
|
||||
|
||||
// This file contains an implementation of proto field accesses using package reflect.
|
||||
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
|
||||
// be used on App Engine.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A structPointer is a pointer to a struct.
|
||||
type structPointer struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
// toStructPointer returns a structPointer equivalent to the given reflect value.
|
||||
// The reflect value must itself be a pointer to a struct.
|
||||
func toStructPointer(v reflect.Value) structPointer {
|
||||
return structPointer{v}
|
||||
}
|
||||
|
||||
// IsNil reports whether p is nil.
|
||||
func structPointer_IsNil(p structPointer) bool {
|
||||
return p.v.IsNil()
|
||||
}
|
||||
|
||||
// Interface returns the struct pointer as an interface value.
|
||||
func structPointer_Interface(p structPointer, _ reflect.Type) interface{} {
|
||||
return p.v.Interface()
|
||||
}
|
||||
|
||||
// A field identifies a field in a struct, accessible from a structPointer.
|
||||
// In this implementation, a field is identified by the sequence of field indices
|
||||
// passed to reflect's FieldByIndex.
|
||||
type field []int
|
||||
|
||||
// toField returns a field equivalent to the given reflect field.
|
||||
func toField(f *reflect.StructField) field {
|
||||
return f.Index
|
||||
}
|
||||
|
||||
// invalidField is an invalid field identifier.
|
||||
var invalidField = field(nil)
|
||||
|
||||
// IsValid reports whether the field identifier is valid.
|
||||
func (f field) IsValid() bool { return f != nil }
|
||||
|
||||
// field returns the given field in the struct as a reflect value.
|
||||
func structPointer_field(p structPointer, f field) reflect.Value {
|
||||
// Special case: an extension map entry with a value of type T
|
||||
// passes a *T to the struct-handling code with a zero field,
|
||||
// expecting that it will be treated as equivalent to *struct{ X T },
|
||||
// which has the same memory layout. We have to handle that case
|
||||
// specially, because reflect will panic if we call FieldByIndex on a
|
||||
// non-struct.
|
||||
if f == nil {
|
||||
return p.v.Elem()
|
||||
}
|
||||
|
||||
return p.v.Elem().FieldByIndex(f)
|
||||
}
|
||||
|
||||
// ifield returns the given field in the struct as an interface value.
|
||||
func structPointer_ifield(p structPointer, f field) interface{} {
|
||||
return structPointer_field(p, f).Addr().Interface()
|
||||
}
|
||||
|
||||
// Bytes returns the address of a []byte field in the struct.
|
||||
func structPointer_Bytes(p structPointer, f field) *[]byte {
|
||||
return structPointer_ifield(p, f).(*[]byte)
|
||||
}
|
||||
|
||||
// BytesSlice returns the address of a [][]byte field in the struct.
|
||||
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
|
||||
return structPointer_ifield(p, f).(*[][]byte)
|
||||
}
|
||||
|
||||
// Bool returns the address of a *bool field in the struct.
|
||||
func structPointer_Bool(p structPointer, f field) **bool {
|
||||
return structPointer_ifield(p, f).(**bool)
|
||||
}
|
||||
|
||||
// BoolVal returns the address of a bool field in the struct.
|
||||
func structPointer_BoolVal(p structPointer, f field) *bool {
|
||||
return structPointer_ifield(p, f).(*bool)
|
||||
}
|
||||
|
||||
// BoolSlice returns the address of a []bool field in the struct.
|
||||
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
|
||||
return structPointer_ifield(p, f).(*[]bool)
|
||||
}
|
||||
|
||||
// String returns the address of a *string field in the struct.
|
||||
func structPointer_String(p structPointer, f field) **string {
|
||||
return structPointer_ifield(p, f).(**string)
|
||||
}
|
||||
|
||||
// StringVal returns the address of a string field in the struct.
|
||||
func structPointer_StringVal(p structPointer, f field) *string {
|
||||
return structPointer_ifield(p, f).(*string)
|
||||
}
|
||||
|
||||
// StringSlice returns the address of a []string field in the struct.
|
||||
func structPointer_StringSlice(p structPointer, f field) *[]string {
|
||||
return structPointer_ifield(p, f).(*[]string)
|
||||
}
|
||||
|
||||
// ExtMap returns the address of an extension map field in the struct.
|
||||
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
|
||||
return structPointer_ifield(p, f).(*map[int32]Extension)
|
||||
}
|
||||
|
||||
// NewAt returns the reflect.Value for a pointer to a field in the struct.
|
||||
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
|
||||
return structPointer_field(p, f).Addr()
|
||||
}
|
||||
|
||||
// SetStructPointer writes a *struct field in the struct.
|
||||
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
|
||||
structPointer_field(p, f).Set(q.v)
|
||||
}
|
||||
|
||||
// GetStructPointer reads a *struct field in the struct.
|
||||
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
|
||||
return structPointer{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// StructPointerSlice the address of a []*struct field in the struct.
|
||||
func structPointer_StructPointerSlice(p structPointer, f field) structPointerSlice {
|
||||
return structPointerSlice{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// A structPointerSlice represents the address of a slice of pointers to structs
|
||||
// (themselves messages or groups). That is, v.Type() is *[]*struct{...}.
|
||||
type structPointerSlice struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func (p structPointerSlice) Len() int { return p.v.Len() }
|
||||
func (p structPointerSlice) Index(i int) structPointer { return structPointer{p.v.Index(i)} }
|
||||
func (p structPointerSlice) Append(q structPointer) {
|
||||
p.v.Set(reflect.Append(p.v, q.v))
|
||||
}
|
||||
|
||||
var (
|
||||
int32Type = reflect.TypeOf(int32(0))
|
||||
uint32Type = reflect.TypeOf(uint32(0))
|
||||
float32Type = reflect.TypeOf(float32(0))
|
||||
int64Type = reflect.TypeOf(int64(0))
|
||||
uint64Type = reflect.TypeOf(uint64(0))
|
||||
float64Type = reflect.TypeOf(float64(0))
|
||||
)
|
||||
|
||||
// A word32 represents a field of type *int32, *uint32, *float32, or *enum.
|
||||
// That is, v.Type() is *int32, *uint32, *float32, or *enum and v is assignable.
|
||||
type word32 struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
// IsNil reports whether p is nil.
|
||||
func word32_IsNil(p word32) bool {
|
||||
return p.v.IsNil()
|
||||
}
|
||||
|
||||
// Set sets p to point at a newly allocated word with bits set to x.
|
||||
func word32_Set(p word32, o *Buffer, x uint32) {
|
||||
t := p.v.Type().Elem()
|
||||
switch t {
|
||||
case int32Type:
|
||||
if len(o.int32s) == 0 {
|
||||
o.int32s = make([]int32, uint32PoolSize)
|
||||
}
|
||||
o.int32s[0] = int32(x)
|
||||
p.v.Set(reflect.ValueOf(&o.int32s[0]))
|
||||
o.int32s = o.int32s[1:]
|
||||
return
|
||||
case uint32Type:
|
||||
if len(o.uint32s) == 0 {
|
||||
o.uint32s = make([]uint32, uint32PoolSize)
|
||||
}
|
||||
o.uint32s[0] = x
|
||||
p.v.Set(reflect.ValueOf(&o.uint32s[0]))
|
||||
o.uint32s = o.uint32s[1:]
|
||||
return
|
||||
case float32Type:
|
||||
if len(o.float32s) == 0 {
|
||||
o.float32s = make([]float32, uint32PoolSize)
|
||||
}
|
||||
o.float32s[0] = math.Float32frombits(x)
|
||||
p.v.Set(reflect.ValueOf(&o.float32s[0]))
|
||||
o.float32s = o.float32s[1:]
|
||||
return
|
||||
}
|
||||
|
||||
// must be enum
|
||||
p.v.Set(reflect.New(t))
|
||||
p.v.Elem().SetInt(int64(int32(x)))
|
||||
}
|
||||
|
||||
// Get gets the bits pointed at by p, as a uint32.
|
||||
func word32_Get(p word32) uint32 {
|
||||
elem := p.v.Elem()
|
||||
switch elem.Kind() {
|
||||
case reflect.Int32:
|
||||
return uint32(elem.Int())
|
||||
case reflect.Uint32:
|
||||
return uint32(elem.Uint())
|
||||
case reflect.Float32:
|
||||
return math.Float32bits(float32(elem.Float()))
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Word32 returns a reference to a *int32, *uint32, *float32, or *enum field in the struct.
|
||||
func structPointer_Word32(p structPointer, f field) word32 {
|
||||
return word32{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// A word32Val represents a field of type int32, uint32, float32, or enum.
|
||||
// That is, v.Type() is int32, uint32, float32, or enum and v is assignable.
|
||||
type word32Val struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
// Set sets *p to x.
|
||||
func word32Val_Set(p word32Val, x uint32) {
|
||||
switch p.v.Type() {
|
||||
case int32Type:
|
||||
p.v.SetInt(int64(x))
|
||||
return
|
||||
case uint32Type:
|
||||
p.v.SetUint(uint64(x))
|
||||
return
|
||||
case float32Type:
|
||||
p.v.SetFloat(float64(math.Float32frombits(x)))
|
||||
return
|
||||
}
|
||||
|
||||
// must be enum
|
||||
p.v.SetInt(int64(int32(x)))
|
||||
}
|
||||
|
||||
// Get gets the bits pointed at by p, as a uint32.
|
||||
func word32Val_Get(p word32Val) uint32 {
|
||||
elem := p.v
|
||||
switch elem.Kind() {
|
||||
case reflect.Int32:
|
||||
return uint32(elem.Int())
|
||||
case reflect.Uint32:
|
||||
return uint32(elem.Uint())
|
||||
case reflect.Float32:
|
||||
return math.Float32bits(float32(elem.Float()))
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Word32Val returns a reference to a int32, uint32, float32, or enum field in the struct.
|
||||
func structPointer_Word32Val(p structPointer, f field) word32Val {
|
||||
return word32Val{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// A word32Slice is a slice of 32-bit values.
|
||||
// That is, v.Type() is []int32, []uint32, []float32, or []enum.
|
||||
type word32Slice struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func (p word32Slice) Append(x uint32) {
|
||||
n, m := p.v.Len(), p.v.Cap()
|
||||
if n < m {
|
||||
p.v.SetLen(n + 1)
|
||||
} else {
|
||||
t := p.v.Type().Elem()
|
||||
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
|
||||
}
|
||||
elem := p.v.Index(n)
|
||||
switch elem.Kind() {
|
||||
case reflect.Int32:
|
||||
elem.SetInt(int64(int32(x)))
|
||||
case reflect.Uint32:
|
||||
elem.SetUint(uint64(x))
|
||||
case reflect.Float32:
|
||||
elem.SetFloat(float64(math.Float32frombits(x)))
|
||||
}
|
||||
}
|
||||
|
||||
func (p word32Slice) Len() int {
|
||||
return p.v.Len()
|
||||
}
|
||||
|
||||
func (p word32Slice) Index(i int) uint32 {
|
||||
elem := p.v.Index(i)
|
||||
switch elem.Kind() {
|
||||
case reflect.Int32:
|
||||
return uint32(elem.Int())
|
||||
case reflect.Uint32:
|
||||
return uint32(elem.Uint())
|
||||
case reflect.Float32:
|
||||
return math.Float32bits(float32(elem.Float()))
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Word32Slice returns a reference to a []int32, []uint32, []float32, or []enum field in the struct.
|
||||
func structPointer_Word32Slice(p structPointer, f field) word32Slice {
|
||||
return word32Slice{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// word64 is like word32 but for 64-bit values.
|
||||
type word64 struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func word64_Set(p word64, o *Buffer, x uint64) {
|
||||
t := p.v.Type().Elem()
|
||||
switch t {
|
||||
case int64Type:
|
||||
if len(o.int64s) == 0 {
|
||||
o.int64s = make([]int64, uint64PoolSize)
|
||||
}
|
||||
o.int64s[0] = int64(x)
|
||||
p.v.Set(reflect.ValueOf(&o.int64s[0]))
|
||||
o.int64s = o.int64s[1:]
|
||||
return
|
||||
case uint64Type:
|
||||
if len(o.uint64s) == 0 {
|
||||
o.uint64s = make([]uint64, uint64PoolSize)
|
||||
}
|
||||
o.uint64s[0] = x
|
||||
p.v.Set(reflect.ValueOf(&o.uint64s[0]))
|
||||
o.uint64s = o.uint64s[1:]
|
||||
return
|
||||
case float64Type:
|
||||
if len(o.float64s) == 0 {
|
||||
o.float64s = make([]float64, uint64PoolSize)
|
||||
}
|
||||
o.float64s[0] = math.Float64frombits(x)
|
||||
p.v.Set(reflect.ValueOf(&o.float64s[0]))
|
||||
o.float64s = o.float64s[1:]
|
||||
return
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func word64_IsNil(p word64) bool {
|
||||
return p.v.IsNil()
|
||||
}
|
||||
|
||||
func word64_Get(p word64) uint64 {
|
||||
elem := p.v.Elem()
|
||||
switch elem.Kind() {
|
||||
case reflect.Int64:
|
||||
return uint64(elem.Int())
|
||||
case reflect.Uint64:
|
||||
return elem.Uint()
|
||||
case reflect.Float64:
|
||||
return math.Float64bits(elem.Float())
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func structPointer_Word64(p structPointer, f field) word64 {
|
||||
return word64{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// word64Val is like word32Val but for 64-bit values.
|
||||
type word64Val struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
|
||||
switch p.v.Type() {
|
||||
case int64Type:
|
||||
p.v.SetInt(int64(x))
|
||||
return
|
||||
case uint64Type:
|
||||
p.v.SetUint(x)
|
||||
return
|
||||
case float64Type:
|
||||
p.v.SetFloat(math.Float64frombits(x))
|
||||
return
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func word64Val_Get(p word64Val) uint64 {
|
||||
elem := p.v
|
||||
switch elem.Kind() {
|
||||
case reflect.Int64:
|
||||
return uint64(elem.Int())
|
||||
case reflect.Uint64:
|
||||
return elem.Uint()
|
||||
case reflect.Float64:
|
||||
return math.Float64bits(elem.Float())
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func structPointer_Word64Val(p structPointer, f field) word64Val {
|
||||
return word64Val{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
type word64Slice struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func (p word64Slice) Append(x uint64) {
|
||||
n, m := p.v.Len(), p.v.Cap()
|
||||
if n < m {
|
||||
p.v.SetLen(n + 1)
|
||||
} else {
|
||||
t := p.v.Type().Elem()
|
||||
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
|
||||
}
|
||||
elem := p.v.Index(n)
|
||||
switch elem.Kind() {
|
||||
case reflect.Int64:
|
||||
elem.SetInt(int64(int64(x)))
|
||||
case reflect.Uint64:
|
||||
elem.SetUint(uint64(x))
|
||||
case reflect.Float64:
|
||||
elem.SetFloat(float64(math.Float64frombits(x)))
|
||||
}
|
||||
}
|
||||
|
||||
func (p word64Slice) Len() int {
|
||||
return p.v.Len()
|
||||
}
|
||||
|
||||
func (p word64Slice) Index(i int) uint64 {
|
||||
elem := p.v.Index(i)
|
||||
switch elem.Kind() {
|
||||
case reflect.Int64:
|
||||
return uint64(elem.Int())
|
||||
case reflect.Uint64:
|
||||
return uint64(elem.Uint())
|
||||
case reflect.Float64:
|
||||
return math.Float64bits(float64(elem.Float()))
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func structPointer_Word64Slice(p structPointer, f field) word64Slice {
|
||||
return word64Slice{structPointer_field(p, f)}
|
||||
}
|
266
Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
266
Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
@ -1,266 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// +build !appengine,!js
|
||||
|
||||
// This file contains the implementation of the proto field accesses using package unsafe.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// NOTE: These type_Foo functions would more idiomatically be methods,
|
||||
// but Go does not allow methods on pointer types, and we must preserve
|
||||
// some pointer type for the garbage collector. We use these
|
||||
// funcs with clunky names as our poor approximation to methods.
|
||||
//
|
||||
// An alternative would be
|
||||
// type structPointer struct { p unsafe.Pointer }
|
||||
// but that does not registerize as well.
|
||||
|
||||
// A structPointer is a pointer to a struct.
|
||||
type structPointer unsafe.Pointer
|
||||
|
||||
// toStructPointer returns a structPointer equivalent to the given reflect value.
|
||||
func toStructPointer(v reflect.Value) structPointer {
|
||||
return structPointer(unsafe.Pointer(v.Pointer()))
|
||||
}
|
||||
|
||||
// IsNil reports whether p is nil.
|
||||
func structPointer_IsNil(p structPointer) bool {
|
||||
return p == nil
|
||||
}
|
||||
|
||||
// Interface returns the struct pointer, assumed to have element type t,
|
||||
// as an interface value.
|
||||
func structPointer_Interface(p structPointer, t reflect.Type) interface{} {
|
||||
return reflect.NewAt(t, unsafe.Pointer(p)).Interface()
|
||||
}
|
||||
|
||||
// A field identifies a field in a struct, accessible from a structPointer.
|
||||
// In this implementation, a field is identified by its byte offset from the start of the struct.
|
||||
type field uintptr
|
||||
|
||||
// toField returns a field equivalent to the given reflect field.
|
||||
func toField(f *reflect.StructField) field {
|
||||
return field(f.Offset)
|
||||
}
|
||||
|
||||
// invalidField is an invalid field identifier.
|
||||
const invalidField = ^field(0)
|
||||
|
||||
// IsValid reports whether the field identifier is valid.
|
||||
func (f field) IsValid() bool {
|
||||
return f != ^field(0)
|
||||
}
|
||||
|
||||
// Bytes returns the address of a []byte field in the struct.
|
||||
func structPointer_Bytes(p structPointer, f field) *[]byte {
|
||||
return (*[]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// BytesSlice returns the address of a [][]byte field in the struct.
|
||||
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
|
||||
return (*[][]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// Bool returns the address of a *bool field in the struct.
|
||||
func structPointer_Bool(p structPointer, f field) **bool {
|
||||
return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// BoolVal returns the address of a bool field in the struct.
|
||||
func structPointer_BoolVal(p structPointer, f field) *bool {
|
||||
return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// BoolSlice returns the address of a []bool field in the struct.
|
||||
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
|
||||
return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// String returns the address of a *string field in the struct.
|
||||
func structPointer_String(p structPointer, f field) **string {
|
||||
return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// StringVal returns the address of a string field in the struct.
|
||||
func structPointer_StringVal(p structPointer, f field) *string {
|
||||
return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// StringSlice returns the address of a []string field in the struct.
|
||||
func structPointer_StringSlice(p structPointer, f field) *[]string {
|
||||
return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// ExtMap returns the address of an extension map field in the struct.
|
||||
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
|
||||
return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// NewAt returns the reflect.Value for a pointer to a field in the struct.
|
||||
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
|
||||
return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f)))
|
||||
}
|
||||
|
||||
// SetStructPointer writes a *struct field in the struct.
|
||||
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
|
||||
*(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q
|
||||
}
|
||||
|
||||
// GetStructPointer reads a *struct field in the struct.
|
||||
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
|
||||
return *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// StructPointerSlice the address of a []*struct field in the struct.
|
||||
func structPointer_StructPointerSlice(p structPointer, f field) *structPointerSlice {
|
||||
return (*structPointerSlice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// A structPointerSlice represents a slice of pointers to structs (themselves submessages or groups).
|
||||
type structPointerSlice []structPointer
|
||||
|
||||
func (v *structPointerSlice) Len() int { return len(*v) }
|
||||
func (v *structPointerSlice) Index(i int) structPointer { return (*v)[i] }
|
||||
func (v *structPointerSlice) Append(p structPointer) { *v = append(*v, p) }
|
||||
|
||||
// A word32 is the address of a "pointer to 32-bit value" field.
|
||||
type word32 **uint32
|
||||
|
||||
// IsNil reports whether *v is nil.
|
||||
func word32_IsNil(p word32) bool {
|
||||
return *p == nil
|
||||
}
|
||||
|
||||
// Set sets *v to point at a newly allocated word set to x.
|
||||
func word32_Set(p word32, o *Buffer, x uint32) {
|
||||
if len(o.uint32s) == 0 {
|
||||
o.uint32s = make([]uint32, uint32PoolSize)
|
||||
}
|
||||
o.uint32s[0] = x
|
||||
*p = &o.uint32s[0]
|
||||
o.uint32s = o.uint32s[1:]
|
||||
}
|
||||
|
||||
// Get gets the value pointed at by *v.
|
||||
func word32_Get(p word32) uint32 {
|
||||
return **p
|
||||
}
|
||||
|
||||
// Word32 returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
|
||||
func structPointer_Word32(p structPointer, f field) word32 {
|
||||
return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
||||
}
|
||||
|
||||
// A word32Val is the address of a 32-bit value field.
|
||||
type word32Val *uint32
|
||||
|
||||
// Set sets *p to x.
|
||||
func word32Val_Set(p word32Val, x uint32) {
|
||||
*p = x
|
||||
}
|
||||
|
||||
// Get gets the value pointed at by p.
|
||||
func word32Val_Get(p word32Val) uint32 {
|
||||
return *p
|
||||
}
|
||||
|
||||
// Word32Val returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
|
||||
func structPointer_Word32Val(p structPointer, f field) word32Val {
|
||||
return word32Val((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
||||
}
|
||||
|
||||
// A word32Slice is a slice of 32-bit values.
|
||||
type word32Slice []uint32
|
||||
|
||||
func (v *word32Slice) Append(x uint32) { *v = append(*v, x) }
|
||||
func (v *word32Slice) Len() int { return len(*v) }
|
||||
func (v *word32Slice) Index(i int) uint32 { return (*v)[i] }
|
||||
|
||||
// Word32Slice returns the address of a []int32, []uint32, []float32, or []enum field in the struct.
|
||||
func structPointer_Word32Slice(p structPointer, f field) *word32Slice {
|
||||
return (*word32Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// word64 is like word32 but for 64-bit values.
|
||||
type word64 **uint64
|
||||
|
||||
func word64_Set(p word64, o *Buffer, x uint64) {
|
||||
if len(o.uint64s) == 0 {
|
||||
o.uint64s = make([]uint64, uint64PoolSize)
|
||||
}
|
||||
o.uint64s[0] = x
|
||||
*p = &o.uint64s[0]
|
||||
o.uint64s = o.uint64s[1:]
|
||||
}
|
||||
|
||||
func word64_IsNil(p word64) bool {
|
||||
return *p == nil
|
||||
}
|
||||
|
||||
func word64_Get(p word64) uint64 {
|
||||
return **p
|
||||
}
|
||||
|
||||
func structPointer_Word64(p structPointer, f field) word64 {
|
||||
return word64((**uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
||||
}
|
||||
|
||||
// word64Val is like word32Val but for 64-bit values.
|
||||
type word64Val *uint64
|
||||
|
||||
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
|
||||
*p = x
|
||||
}
|
||||
|
||||
func word64Val_Get(p word64Val) uint64 {
|
||||
return *p
|
||||
}
|
||||
|
||||
func structPointer_Word64Val(p structPointer, f field) word64Val {
|
||||
return word64Val((*uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
||||
}
|
||||
|
||||
// word64Slice is like word32Slice but for 64-bit values.
|
||||
type word64Slice []uint64
|
||||
|
||||
func (v *word64Slice) Append(x uint64) { *v = append(*v, x) }
|
||||
func (v *word64Slice) Len() int { return len(*v) }
|
||||
func (v *word64Slice) Index(i int) uint64 { return (*v)[i] }
|
||||
|
||||
func structPointer_Word64Slice(p structPointer, f field) *word64Slice {
|
||||
return (*word64Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
850
Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go
generated
vendored
850
Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go
generated
vendored
@ -1,850 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
/*
|
||||
* Routines for encoding data into the wire format for protocol buffers.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const debug bool = false
|
||||
|
||||
// Constants that identify the encoding of a value on the wire.
|
||||
const (
|
||||
WireVarint = 0
|
||||
WireFixed64 = 1
|
||||
WireBytes = 2
|
||||
WireStartGroup = 3
|
||||
WireEndGroup = 4
|
||||
WireFixed32 = 5
|
||||
)
|
||||
|
||||
const startSize = 10 // initial slice/string sizes
|
||||
|
||||
// Encoders are defined in encode.go
|
||||
// An encoder outputs the full representation of a field, including its
|
||||
// tag and encoder type.
|
||||
type encoder func(p *Buffer, prop *Properties, base structPointer) error
|
||||
|
||||
// A valueEncoder encodes a single integer in a particular encoding.
|
||||
type valueEncoder func(o *Buffer, x uint64) error
|
||||
|
||||
// Sizers are defined in encode.go
|
||||
// A sizer returns the encoded size of a field, including its tag and encoder
|
||||
// type.
|
||||
type sizer func(prop *Properties, base structPointer) int
|
||||
|
||||
// A valueSizer returns the encoded size of a single integer in a particular
|
||||
// encoding.
|
||||
type valueSizer func(x uint64) int
|
||||
|
||||
// Decoders are defined in decode.go
|
||||
// A decoder creates a value from its wire representation.
|
||||
// Unrecognized subelements are saved in unrec.
|
||||
type decoder func(p *Buffer, prop *Properties, base structPointer) error
|
||||
|
||||
// A valueDecoder decodes a single integer in a particular encoding.
|
||||
type valueDecoder func(o *Buffer) (x uint64, err error)
|
||||
|
||||
// A oneofMarshaler does the marshaling for all oneof fields in a message.
|
||||
type oneofMarshaler func(Message, *Buffer) error
|
||||
|
||||
// A oneofUnmarshaler does the unmarshaling for a oneof field in a message.
|
||||
type oneofUnmarshaler func(Message, int, int, *Buffer) (bool, error)
|
||||
|
||||
// A oneofSizer does the sizing for all oneof fields in a message.
|
||||
type oneofSizer func(Message) int
|
||||
|
||||
// tagMap is an optimization over map[int]int for typical protocol buffer
|
||||
// use-cases. Encoded protocol buffers are often in tag order with small tag
|
||||
// numbers.
|
||||
type tagMap struct {
|
||||
fastTags []int
|
||||
slowTags map[int]int
|
||||
}
|
||||
|
||||
// tagMapFastLimit is the upper bound on the tag number that will be stored in
|
||||
// the tagMap slice rather than its map.
|
||||
const tagMapFastLimit = 1024
|
||||
|
||||
func (p *tagMap) get(t int) (int, bool) {
|
||||
if t > 0 && t < tagMapFastLimit {
|
||||
if t >= len(p.fastTags) {
|
||||
return 0, false
|
||||
}
|
||||
fi := p.fastTags[t]
|
||||
return fi, fi >= 0
|
||||
}
|
||||
fi, ok := p.slowTags[t]
|
||||
return fi, ok
|
||||
}
|
||||
|
||||
func (p *tagMap) put(t int, fi int) {
|
||||
if t > 0 && t < tagMapFastLimit {
|
||||
for len(p.fastTags) < t+1 {
|
||||
p.fastTags = append(p.fastTags, -1)
|
||||
}
|
||||
p.fastTags[t] = fi
|
||||
return
|
||||
}
|
||||
if p.slowTags == nil {
|
||||
p.slowTags = make(map[int]int)
|
||||
}
|
||||
p.slowTags[t] = fi
|
||||
}
|
||||
|
||||
// StructProperties represents properties for all the fields of a struct.
|
||||
// decoderTags and decoderOrigNames should only be used by the decoder.
|
||||
type StructProperties struct {
|
||||
Prop []*Properties // properties for each field
|
||||
reqCount int // required count
|
||||
decoderTags tagMap // map from proto tag to struct field number
|
||||
decoderOrigNames map[string]int // map from original name to struct field number
|
||||
order []int // list of struct field numbers in tag order
|
||||
unrecField field // field id of the XXX_unrecognized []byte field
|
||||
extendable bool // is this an extendable proto
|
||||
|
||||
oneofMarshaler oneofMarshaler
|
||||
oneofUnmarshaler oneofUnmarshaler
|
||||
oneofSizer oneofSizer
|
||||
stype reflect.Type
|
||||
|
||||
// OneofTypes contains information about the oneof fields in this message.
|
||||
// It is keyed by the original name of a field.
|
||||
OneofTypes map[string]*OneofProperties
|
||||
}
|
||||
|
||||
// OneofProperties represents information about a specific field in a oneof.
|
||||
type OneofProperties struct {
|
||||
Type reflect.Type // pointer to generated struct type for this oneof field
|
||||
Field int // struct field number of the containing oneof in the message
|
||||
Prop *Properties
|
||||
}
|
||||
|
||||
// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
|
||||
// See encode.go, (*Buffer).enc_struct.
|
||||
|
||||
func (sp *StructProperties) Len() int { return len(sp.order) }
|
||||
func (sp *StructProperties) Less(i, j int) bool {
|
||||
return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag
|
||||
}
|
||||
func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] }
|
||||
|
||||
// Properties represents the protocol-specific behavior of a single struct field.
|
||||
type Properties struct {
|
||||
Name string // name of the field, for error messages
|
||||
OrigName string // original name before protocol compiler (always set)
|
||||
JSONName string // name to use for JSON; determined by protoc
|
||||
Wire string
|
||||
WireType int
|
||||
Tag int
|
||||
Required bool
|
||||
Optional bool
|
||||
Repeated bool
|
||||
Packed bool // relevant for repeated primitives only
|
||||
Enum string // set for enum types only
|
||||
proto3 bool // whether this is known to be a proto3 field; set for []byte only
|
||||
oneof bool // whether this is a oneof field
|
||||
|
||||
Default string // default value
|
||||
HasDefault bool // whether an explicit default was provided
|
||||
def_uint64 uint64
|
||||
|
||||
enc encoder
|
||||
valEnc valueEncoder // set for bool and numeric types only
|
||||
field field
|
||||
tagcode []byte // encoding of EncodeVarint((Tag<<3)|WireType)
|
||||
tagbuf [8]byte
|
||||
stype reflect.Type // set for struct types only
|
||||
sprop *StructProperties // set for struct types only
|
||||
isMarshaler bool
|
||||
isUnmarshaler bool
|
||||
|
||||
mtype reflect.Type // set for map types only
|
||||
mkeyprop *Properties // set for map types only
|
||||
mvalprop *Properties // set for map types only
|
||||
|
||||
size sizer
|
||||
valSize valueSizer // set for bool and numeric types only
|
||||
|
||||
dec decoder
|
||||
valDec valueDecoder // set for bool and numeric types only
|
||||
|
||||
// If this is a packable field, this will be the decoder for the packed version of the field.
|
||||
packedDec decoder
|
||||
}
|
||||
|
||||
// String formats the properties in the protobuf struct field tag style.
|
||||
func (p *Properties) String() string {
|
||||
s := p.Wire
|
||||
s = ","
|
||||
s += strconv.Itoa(p.Tag)
|
||||
if p.Required {
|
||||
s += ",req"
|
||||
}
|
||||
if p.Optional {
|
||||
s += ",opt"
|
||||
}
|
||||
if p.Repeated {
|
||||
s += ",rep"
|
||||
}
|
||||
if p.Packed {
|
||||
s += ",packed"
|
||||
}
|
||||
s += ",name=" + p.OrigName
|
||||
if p.JSONName != p.OrigName {
|
||||
s += ",json=" + p.JSONName
|
||||
}
|
||||
if p.proto3 {
|
||||
s += ",proto3"
|
||||
}
|
||||
if p.oneof {
|
||||
s += ",oneof"
|
||||
}
|
||||
if len(p.Enum) > 0 {
|
||||
s += ",enum=" + p.Enum
|
||||
}
|
||||
if p.HasDefault {
|
||||
s += ",def=" + p.Default
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
||||
func (p *Properties) Parse(s string) {
|
||||
// "bytes,49,opt,name=foo,def=hello!"
|
||||
fields := strings.Split(s, ",") // breaks def=, but handled below.
|
||||
if len(fields) < 2 {
|
||||
fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
|
||||
return
|
||||
}
|
||||
|
||||
p.Wire = fields[0]
|
||||
switch p.Wire {
|
||||
case "varint":
|
||||
p.WireType = WireVarint
|
||||
p.valEnc = (*Buffer).EncodeVarint
|
||||
p.valDec = (*Buffer).DecodeVarint
|
||||
p.valSize = sizeVarint
|
||||
case "fixed32":
|
||||
p.WireType = WireFixed32
|
||||
p.valEnc = (*Buffer).EncodeFixed32
|
||||
p.valDec = (*Buffer).DecodeFixed32
|
||||
p.valSize = sizeFixed32
|
||||
case "fixed64":
|
||||
p.WireType = WireFixed64
|
||||
p.valEnc = (*Buffer).EncodeFixed64
|
||||
p.valDec = (*Buffer).DecodeFixed64
|
||||
p.valSize = sizeFixed64
|
||||
case "zigzag32":
|
||||
p.WireType = WireVarint
|
||||
p.valEnc = (*Buffer).EncodeZigzag32
|
||||
p.valDec = (*Buffer).DecodeZigzag32
|
||||
p.valSize = sizeZigzag32
|
||||
case "zigzag64":
|
||||
p.WireType = WireVarint
|
||||
p.valEnc = (*Buffer).EncodeZigzag64
|
||||
p.valDec = (*Buffer).DecodeZigzag64
|
||||
p.valSize = sizeZigzag64
|
||||
case "bytes", "group":
|
||||
p.WireType = WireBytes
|
||||
// no numeric converter for non-numeric types
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
p.Tag, err = strconv.Atoi(fields[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 2; i < len(fields); i++ {
|
||||
f := fields[i]
|
||||
switch {
|
||||
case f == "req":
|
||||
p.Required = true
|
||||
case f == "opt":
|
||||
p.Optional = true
|
||||
case f == "rep":
|
||||
p.Repeated = true
|
||||
case f == "packed":
|
||||
p.Packed = true
|
||||
case strings.HasPrefix(f, "name="):
|
||||
p.OrigName = f[5:]
|
||||
case strings.HasPrefix(f, "json="):
|
||||
p.JSONName = f[5:]
|
||||
case strings.HasPrefix(f, "enum="):
|
||||
p.Enum = f[5:]
|
||||
case f == "proto3":
|
||||
p.proto3 = true
|
||||
case f == "oneof":
|
||||
p.oneof = true
|
||||
case strings.HasPrefix(f, "def="):
|
||||
p.HasDefault = true
|
||||
p.Default = f[4:] // rest of string
|
||||
if i+1 < len(fields) {
|
||||
// Commas aren't escaped, and def is always last.
|
||||
p.Default += "," + strings.Join(fields[i+1:], ",")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logNoSliceEnc(t1, t2 reflect.Type) {
|
||||
fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2)
|
||||
}
|
||||
|
||||
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
|
||||
|
||||
// Initialize the fields for encoding and decoding.
|
||||
func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
|
||||
p.enc = nil
|
||||
p.dec = nil
|
||||
p.size = nil
|
||||
|
||||
switch t1 := typ; t1.Kind() {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1)
|
||||
|
||||
// proto3 scalar types
|
||||
|
||||
case reflect.Bool:
|
||||
p.enc = (*Buffer).enc_proto3_bool
|
||||
p.dec = (*Buffer).dec_proto3_bool
|
||||
p.size = size_proto3_bool
|
||||
case reflect.Int32:
|
||||
p.enc = (*Buffer).enc_proto3_int32
|
||||
p.dec = (*Buffer).dec_proto3_int32
|
||||
p.size = size_proto3_int32
|
||||
case reflect.Uint32:
|
||||
p.enc = (*Buffer).enc_proto3_uint32
|
||||
p.dec = (*Buffer).dec_proto3_int32 // can reuse
|
||||
p.size = size_proto3_uint32
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
p.enc = (*Buffer).enc_proto3_int64
|
||||
p.dec = (*Buffer).dec_proto3_int64
|
||||
p.size = size_proto3_int64
|
||||
case reflect.Float32:
|
||||
p.enc = (*Buffer).enc_proto3_uint32 // can just treat them as bits
|
||||
p.dec = (*Buffer).dec_proto3_int32
|
||||
p.size = size_proto3_uint32
|
||||
case reflect.Float64:
|
||||
p.enc = (*Buffer).enc_proto3_int64 // can just treat them as bits
|
||||
p.dec = (*Buffer).dec_proto3_int64
|
||||
p.size = size_proto3_int64
|
||||
case reflect.String:
|
||||
p.enc = (*Buffer).enc_proto3_string
|
||||
p.dec = (*Buffer).dec_proto3_string
|
||||
p.size = size_proto3_string
|
||||
|
||||
case reflect.Ptr:
|
||||
switch t2 := t1.Elem(); t2.Kind() {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: no encoder function for %v -> %v\n", t1, t2)
|
||||
break
|
||||
case reflect.Bool:
|
||||
p.enc = (*Buffer).enc_bool
|
||||
p.dec = (*Buffer).dec_bool
|
||||
p.size = size_bool
|
||||
case reflect.Int32:
|
||||
p.enc = (*Buffer).enc_int32
|
||||
p.dec = (*Buffer).dec_int32
|
||||
p.size = size_int32
|
||||
case reflect.Uint32:
|
||||
p.enc = (*Buffer).enc_uint32
|
||||
p.dec = (*Buffer).dec_int32 // can reuse
|
||||
p.size = size_uint32
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
p.enc = (*Buffer).enc_int64
|
||||
p.dec = (*Buffer).dec_int64
|
||||
p.size = size_int64
|
||||
case reflect.Float32:
|
||||
p.enc = (*Buffer).enc_uint32 // can just treat them as bits
|
||||
p.dec = (*Buffer).dec_int32
|
||||
p.size = size_uint32
|
||||
case reflect.Float64:
|
||||
p.enc = (*Buffer).enc_int64 // can just treat them as bits
|
||||
p.dec = (*Buffer).dec_int64
|
||||
p.size = size_int64
|
||||
case reflect.String:
|
||||
p.enc = (*Buffer).enc_string
|
||||
p.dec = (*Buffer).dec_string
|
||||
p.size = size_string
|
||||
case reflect.Struct:
|
||||
p.stype = t1.Elem()
|
||||
p.isMarshaler = isMarshaler(t1)
|
||||
p.isUnmarshaler = isUnmarshaler(t1)
|
||||
if p.Wire == "bytes" {
|
||||
p.enc = (*Buffer).enc_struct_message
|
||||
p.dec = (*Buffer).dec_struct_message
|
||||
p.size = size_struct_message
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_struct_group
|
||||
p.dec = (*Buffer).dec_struct_group
|
||||
p.size = size_struct_group
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
switch t2 := t1.Elem(); t2.Kind() {
|
||||
default:
|
||||
logNoSliceEnc(t1, t2)
|
||||
break
|
||||
case reflect.Bool:
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_bool
|
||||
p.size = size_slice_packed_bool
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_bool
|
||||
p.size = size_slice_bool
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_bool
|
||||
p.packedDec = (*Buffer).dec_slice_packed_bool
|
||||
case reflect.Int32:
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_int32
|
||||
p.size = size_slice_packed_int32
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_int32
|
||||
p.size = size_slice_int32
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int32
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
||||
case reflect.Uint32:
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_uint32
|
||||
p.size = size_slice_packed_uint32
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_uint32
|
||||
p.size = size_slice_uint32
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int32
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_int64
|
||||
p.size = size_slice_packed_int64
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_int64
|
||||
p.size = size_slice_int64
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int64
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int64
|
||||
case reflect.Uint8:
|
||||
p.enc = (*Buffer).enc_slice_byte
|
||||
p.dec = (*Buffer).dec_slice_byte
|
||||
p.size = size_slice_byte
|
||||
// This is a []byte, which is either a bytes field,
|
||||
// or the value of a map field. In the latter case,
|
||||
// we always encode an empty []byte, so we should not
|
||||
// use the proto3 enc/size funcs.
|
||||
// f == nil iff this is the key/value of a map field.
|
||||
if p.proto3 && f != nil {
|
||||
p.enc = (*Buffer).enc_proto3_slice_byte
|
||||
p.size = size_proto3_slice_byte
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch t2.Bits() {
|
||||
case 32:
|
||||
// can just treat them as bits
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_uint32
|
||||
p.size = size_slice_packed_uint32
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_uint32
|
||||
p.size = size_slice_uint32
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int32
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
||||
case 64:
|
||||
// can just treat them as bits
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_int64
|
||||
p.size = size_slice_packed_int64
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_int64
|
||||
p.size = size_slice_int64
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int64
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int64
|
||||
default:
|
||||
logNoSliceEnc(t1, t2)
|
||||
break
|
||||
}
|
||||
case reflect.String:
|
||||
p.enc = (*Buffer).enc_slice_string
|
||||
p.dec = (*Buffer).dec_slice_string
|
||||
p.size = size_slice_string
|
||||
case reflect.Ptr:
|
||||
switch t3 := t2.Elem(); t3.Kind() {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T -> %T\n", t1, t2, t3)
|
||||
break
|
||||
case reflect.Struct:
|
||||
p.stype = t2.Elem()
|
||||
p.isMarshaler = isMarshaler(t2)
|
||||
p.isUnmarshaler = isUnmarshaler(t2)
|
||||
if p.Wire == "bytes" {
|
||||
p.enc = (*Buffer).enc_slice_struct_message
|
||||
p.dec = (*Buffer).dec_slice_struct_message
|
||||
p.size = size_slice_struct_message
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_struct_group
|
||||
p.dec = (*Buffer).dec_slice_struct_group
|
||||
p.size = size_slice_struct_group
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
switch t2.Elem().Kind() {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: no slice elem oenc for %T -> %T -> %T\n", t1, t2, t2.Elem())
|
||||
break
|
||||
case reflect.Uint8:
|
||||
p.enc = (*Buffer).enc_slice_slice_byte
|
||||
p.dec = (*Buffer).dec_slice_slice_byte
|
||||
p.size = size_slice_slice_byte
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
p.enc = (*Buffer).enc_new_map
|
||||
p.dec = (*Buffer).dec_new_map
|
||||
p.size = size_new_map
|
||||
|
||||
p.mtype = t1
|
||||
p.mkeyprop = &Properties{}
|
||||
p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
||||
p.mvalprop = &Properties{}
|
||||
vtype := p.mtype.Elem()
|
||||
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
||||
// The value type is not a message (*T) or bytes ([]byte),
|
||||
// so we need encoders for the pointer to this type.
|
||||
vtype = reflect.PtrTo(vtype)
|
||||
}
|
||||
p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
||||
}
|
||||
|
||||
// precalculate tag code
|
||||
wire := p.WireType
|
||||
if p.Packed {
|
||||
wire = WireBytes
|
||||
}
|
||||
x := uint32(p.Tag)<<3 | uint32(wire)
|
||||
i := 0
|
||||
for i = 0; x > 127; i++ {
|
||||
p.tagbuf[i] = 0x80 | uint8(x&0x7F)
|
||||
x >>= 7
|
||||
}
|
||||
p.tagbuf[i] = uint8(x)
|
||||
p.tagcode = p.tagbuf[0 : i+1]
|
||||
|
||||
if p.stype != nil {
|
||||
if lockGetProp {
|
||||
p.sprop = GetProperties(p.stype)
|
||||
} else {
|
||||
p.sprop = getPropertiesLocked(p.stype)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
// isMarshaler reports whether type t implements Marshaler.
|
||||
func isMarshaler(t reflect.Type) bool {
|
||||
// We're checking for (likely) pointer-receiver methods
|
||||
// so if t is not a pointer, something is very wrong.
|
||||
// The calls above only invoke isMarshaler on pointer types.
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("proto: misuse of isMarshaler")
|
||||
}
|
||||
return t.Implements(marshalerType)
|
||||
}
|
||||
|
||||
// isUnmarshaler reports whether type t implements Unmarshaler.
|
||||
func isUnmarshaler(t reflect.Type) bool {
|
||||
// We're checking for (likely) pointer-receiver methods
|
||||
// so if t is not a pointer, something is very wrong.
|
||||
// The calls above only invoke isUnmarshaler on pointer types.
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("proto: misuse of isUnmarshaler")
|
||||
}
|
||||
return t.Implements(unmarshalerType)
|
||||
}
|
||||
|
||||
// Init populates the properties from a protocol buffer struct tag.
|
||||
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
||||
p.init(typ, name, tag, f, true)
|
||||
}
|
||||
|
||||
func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) {
|
||||
// "bytes,49,opt,def=hello!"
|
||||
p.Name = name
|
||||
p.OrigName = name
|
||||
if f != nil {
|
||||
p.field = toField(f)
|
||||
}
|
||||
if tag == "" {
|
||||
return
|
||||
}
|
||||
p.Parse(tag)
|
||||
p.setEncAndDec(typ, f, lockGetProp)
|
||||
}
|
||||
|
||||
var (
|
||||
propertiesMu sync.RWMutex
|
||||
propertiesMap = make(map[reflect.Type]*StructProperties)
|
||||
)
|
||||
|
||||
// GetProperties returns the list of properties for the type represented by t.
|
||||
// t must represent a generated struct type of a protocol message.
|
||||
func GetProperties(t reflect.Type) *StructProperties {
|
||||
if t.Kind() != reflect.Struct {
|
||||
panic("proto: type must have kind struct")
|
||||
}
|
||||
|
||||
// Most calls to GetProperties in a long-running program will be
|
||||
// retrieving details for types we have seen before.
|
||||
propertiesMu.RLock()
|
||||
sprop, ok := propertiesMap[t]
|
||||
propertiesMu.RUnlock()
|
||||
if ok {
|
||||
if collectStats {
|
||||
stats.Chit++
|
||||
}
|
||||
return sprop
|
||||
}
|
||||
|
||||
propertiesMu.Lock()
|
||||
sprop = getPropertiesLocked(t)
|
||||
propertiesMu.Unlock()
|
||||
return sprop
|
||||
}
|
||||
|
||||
// getPropertiesLocked requires that propertiesMu is held.
|
||||
func getPropertiesLocked(t reflect.Type) *StructProperties {
|
||||
if prop, ok := propertiesMap[t]; ok {
|
||||
if collectStats {
|
||||
stats.Chit++
|
||||
}
|
||||
return prop
|
||||
}
|
||||
if collectStats {
|
||||
stats.Cmiss++
|
||||
}
|
||||
|
||||
prop := new(StructProperties)
|
||||
// in case of recursive protos, fill this in now.
|
||||
propertiesMap[t] = prop
|
||||
|
||||
// build properties
|
||||
prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType)
|
||||
prop.unrecField = invalidField
|
||||
prop.Prop = make([]*Properties, t.NumField())
|
||||
prop.order = make([]int, t.NumField())
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
p := new(Properties)
|
||||
name := f.Name
|
||||
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
|
||||
|
||||
if f.Name == "XXX_extensions" { // special case
|
||||
p.enc = (*Buffer).enc_map
|
||||
p.dec = nil // not needed
|
||||
p.size = size_map
|
||||
}
|
||||
if f.Name == "XXX_unrecognized" { // special case
|
||||
prop.unrecField = toField(&f)
|
||||
}
|
||||
oneof := f.Tag.Get("protobuf_oneof") // special case
|
||||
if oneof != "" {
|
||||
// Oneof fields don't use the traditional protobuf tag.
|
||||
p.OrigName = oneof
|
||||
}
|
||||
prop.Prop[i] = p
|
||||
prop.order[i] = i
|
||||
if debug {
|
||||
print(i, " ", f.Name, " ", t.String(), " ")
|
||||
if p.Tag > 0 {
|
||||
print(p.String())
|
||||
}
|
||||
print("\n")
|
||||
}
|
||||
if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") && oneof == "" {
|
||||
fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
|
||||
}
|
||||
}
|
||||
|
||||
// Re-order prop.order.
|
||||
sort.Sort(prop)
|
||||
|
||||
type oneofMessage interface {
|
||||
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
||||
}
|
||||
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
|
||||
var oots []interface{}
|
||||
prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs()
|
||||
prop.stype = t
|
||||
|
||||
// Interpret oneof metadata.
|
||||
prop.OneofTypes = make(map[string]*OneofProperties)
|
||||
for _, oot := range oots {
|
||||
oop := &OneofProperties{
|
||||
Type: reflect.ValueOf(oot).Type(), // *T
|
||||
Prop: new(Properties),
|
||||
}
|
||||
sft := oop.Type.Elem().Field(0)
|
||||
oop.Prop.Name = sft.Name
|
||||
oop.Prop.Parse(sft.Tag.Get("protobuf"))
|
||||
// There will be exactly one interface field that
|
||||
// this new value is assignable to.
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
if f.Type.Kind() != reflect.Interface {
|
||||
continue
|
||||
}
|
||||
if !oop.Type.AssignableTo(f.Type) {
|
||||
continue
|
||||
}
|
||||
oop.Field = i
|
||||
break
|
||||
}
|
||||
prop.OneofTypes[oop.Prop.OrigName] = oop
|
||||
}
|
||||
}
|
||||
|
||||
// build required counts
|
||||
// build tags
|
||||
reqCount := 0
|
||||
prop.decoderOrigNames = make(map[string]int)
|
||||
for i, p := range prop.Prop {
|
||||
if strings.HasPrefix(p.Name, "XXX_") {
|
||||
// Internal fields should not appear in tags/origNames maps.
|
||||
// They are handled specially when encoding and decoding.
|
||||
continue
|
||||
}
|
||||
if p.Required {
|
||||
reqCount++
|
||||
}
|
||||
prop.decoderTags.put(p.Tag, i)
|
||||
prop.decoderOrigNames[p.OrigName] = i
|
||||
}
|
||||
prop.reqCount = reqCount
|
||||
|
||||
return prop
|
||||
}
|
||||
|
||||
// Return the Properties object for the x[0]'th field of the structure.
|
||||
func propByIndex(t reflect.Type, x []int) *Properties {
|
||||
if len(x) != 1 {
|
||||
fmt.Fprintf(os.Stderr, "proto: field index dimension %d (not 1) for type %s\n", len(x), t)
|
||||
return nil
|
||||
}
|
||||
prop := GetProperties(t)
|
||||
return prop.Prop[x[0]]
|
||||
}
|
||||
|
||||
// Get the address and type of a pointer to a struct from an interface.
|
||||
func getbase(pb Message) (t reflect.Type, b structPointer, err error) {
|
||||
if pb == nil {
|
||||
err = ErrNil
|
||||
return
|
||||
}
|
||||
// get the reflect type of the pointer to the struct.
|
||||
t = reflect.TypeOf(pb)
|
||||
// get the address of the struct.
|
||||
value := reflect.ValueOf(pb)
|
||||
b = toStructPointer(value)
|
||||
return
|
||||
}
|
||||
|
||||
// A global registry of enum types.
|
||||
// The generated code will register the generated maps by calling RegisterEnum.
|
||||
|
||||
var enumValueMaps = make(map[string]map[string]int32)
|
||||
|
||||
// RegisterEnum is called from the generated code to install the enum descriptor
|
||||
// maps into the global table to aid parsing text format protocol buffers.
|
||||
func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) {
|
||||
if _, ok := enumValueMaps[typeName]; ok {
|
||||
panic("proto: duplicate enum registered: " + typeName)
|
||||
}
|
||||
enumValueMaps[typeName] = valueMap
|
||||
}
|
||||
|
||||
// EnumValueMap returns the mapping from names to integers of the
|
||||
// enum type enumType, or a nil if not found.
|
||||
func EnumValueMap(enumType string) map[string]int32 {
|
||||
return enumValueMaps[enumType]
|
||||
}
|
||||
|
||||
// A registry of all linked message types.
|
||||
// The string is a fully-qualified proto name ("pkg.Message").
|
||||
var (
|
||||
protoTypes = make(map[string]reflect.Type)
|
||||
revProtoTypes = make(map[reflect.Type]string)
|
||||
)
|
||||
|
||||
// RegisterType is called from generated code and maps from the fully qualified
|
||||
// proto name to the type (pointer to struct) of the protocol buffer.
|
||||
func RegisterType(x Message, name string) {
|
||||
if _, ok := protoTypes[name]; ok {
|
||||
// TODO: Some day, make this a panic.
|
||||
log.Printf("proto: duplicate proto type registered: %s", name)
|
||||
return
|
||||
}
|
||||
t := reflect.TypeOf(x)
|
||||
protoTypes[name] = t
|
||||
revProtoTypes[t] = name
|
||||
}
|
||||
|
||||
// MessageName returns the fully-qualified proto name for the given message type.
|
||||
func MessageName(x Message) string { return revProtoTypes[reflect.TypeOf(x)] }
|
||||
|
||||
// MessageType returns the message type (pointer to struct) for a named message.
|
||||
func MessageType(name string) reflect.Type { return protoTypes[name] }
|
849
Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go
generated
vendored
849
Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go
generated
vendored
@ -1,849 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
// Functions for writing the text protocol buffer format.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
newline = []byte("\n")
|
||||
spaces = []byte(" ")
|
||||
gtNewline = []byte(">\n")
|
||||
endBraceNewline = []byte("}\n")
|
||||
backslashN = []byte{'\\', 'n'}
|
||||
backslashR = []byte{'\\', 'r'}
|
||||
backslashT = []byte{'\\', 't'}
|
||||
backslashDQ = []byte{'\\', '"'}
|
||||
backslashBS = []byte{'\\', '\\'}
|
||||
posInf = []byte("inf")
|
||||
negInf = []byte("-inf")
|
||||
nan = []byte("nan")
|
||||
)
|
||||
|
||||
type writer interface {
|
||||
io.Writer
|
||||
WriteByte(byte) error
|
||||
}
|
||||
|
||||
// textWriter is an io.Writer that tracks its indentation level.
|
||||
type textWriter struct {
|
||||
ind int
|
||||
complete bool // if the current position is a complete line
|
||||
compact bool // whether to write out as a one-liner
|
||||
w writer
|
||||
}
|
||||
|
||||
func (w *textWriter) WriteString(s string) (n int, err error) {
|
||||
if !strings.Contains(s, "\n") {
|
||||
if !w.compact && w.complete {
|
||||
w.writeIndent()
|
||||
}
|
||||
w.complete = false
|
||||
return io.WriteString(w.w, s)
|
||||
}
|
||||
// WriteString is typically called without newlines, so this
|
||||
// codepath and its copy are rare. We copy to avoid
|
||||
// duplicating all of Write's logic here.
|
||||
return w.Write([]byte(s))
|
||||
}
|
||||
|
||||
func (w *textWriter) Write(p []byte) (n int, err error) {
|
||||
newlines := bytes.Count(p, newline)
|
||||
if newlines == 0 {
|
||||
if !w.compact && w.complete {
|
||||
w.writeIndent()
|
||||
}
|
||||
n, err = w.w.Write(p)
|
||||
w.complete = false
|
||||
return n, err
|
||||
}
|
||||
|
||||
frags := bytes.SplitN(p, newline, newlines+1)
|
||||
if w.compact {
|
||||
for i, frag := range frags {
|
||||
if i > 0 {
|
||||
if err := w.w.WriteByte(' '); err != nil {
|
||||
return n, err
|
||||
}
|
||||
n++
|
||||
}
|
||||
nn, err := w.w.Write(frag)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
for i, frag := range frags {
|
||||
if w.complete {
|
||||
w.writeIndent()
|
||||
}
|
||||
nn, err := w.w.Write(frag)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if i+1 < len(frags) {
|
||||
if err := w.w.WriteByte('\n'); err != nil {
|
||||
return n, err
|
||||
}
|
||||
n++
|
||||
}
|
||||
}
|
||||
w.complete = len(frags[len(frags)-1]) == 0
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (w *textWriter) WriteByte(c byte) error {
|
||||
if w.compact && c == '\n' {
|
||||
c = ' '
|
||||
}
|
||||
if !w.compact && w.complete {
|
||||
w.writeIndent()
|
||||
}
|
||||
err := w.w.WriteByte(c)
|
||||
w.complete = c == '\n'
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *textWriter) indent() { w.ind++ }
|
||||
|
||||
func (w *textWriter) unindent() {
|
||||
if w.ind == 0 {
|
||||
log.Printf("proto: textWriter unindented too far")
|
||||
return
|
||||
}
|
||||
w.ind--
|
||||
}
|
||||
|
||||
func writeName(w *textWriter, props *Properties) error {
|
||||
if _, err := w.WriteString(props.OrigName); err != nil {
|
||||
return err
|
||||
}
|
||||
if props.Wire != "group" {
|
||||
return w.WriteByte(':')
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// raw is the interface satisfied by RawMessage.
|
||||
type raw interface {
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
func requiresQuotes(u string) bool {
|
||||
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
|
||||
for _, ch := range u {
|
||||
switch {
|
||||
case ch == '.' || ch == '/' || ch == '_':
|
||||
continue
|
||||
case '0' <= ch && ch <= '9':
|
||||
continue
|
||||
case 'A' <= ch && ch <= 'Z':
|
||||
continue
|
||||
case 'a' <= ch && ch <= 'z':
|
||||
continue
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isAny reports whether sv is a google.protobuf.Any message
|
||||
func isAny(sv reflect.Value) bool {
|
||||
type wkt interface {
|
||||
XXX_WellKnownType() string
|
||||
}
|
||||
t, ok := sv.Addr().Interface().(wkt)
|
||||
return ok && t.XXX_WellKnownType() == "Any"
|
||||
}
|
||||
|
||||
// writeProto3Any writes an expanded google.protobuf.Any message.
|
||||
//
|
||||
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
|
||||
// required messages are not linked in).
|
||||
//
|
||||
// It returns (true, error) when sv was written in expanded format or an error
|
||||
// was encountered.
|
||||
func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
|
||||
turl := sv.FieldByName("TypeUrl")
|
||||
val := sv.FieldByName("Value")
|
||||
if !turl.IsValid() || !val.IsValid() {
|
||||
return true, errors.New("proto: invalid google.protobuf.Any message")
|
||||
}
|
||||
|
||||
b, ok := val.Interface().([]byte)
|
||||
if !ok {
|
||||
return true, errors.New("proto: invalid google.protobuf.Any message")
|
||||
}
|
||||
|
||||
parts := strings.Split(turl.String(), "/")
|
||||
mt := MessageType(parts[len(parts)-1])
|
||||
if mt == nil {
|
||||
return false, nil
|
||||
}
|
||||
m := reflect.New(mt.Elem())
|
||||
if err := Unmarshal(b, m.Interface().(Message)); err != nil {
|
||||
return false, nil
|
||||
}
|
||||
w.Write([]byte("["))
|
||||
u := turl.String()
|
||||
if requiresQuotes(u) {
|
||||
writeString(w, u)
|
||||
} else {
|
||||
w.Write([]byte(u))
|
||||
}
|
||||
if w.compact {
|
||||
w.Write([]byte("]:<"))
|
||||
} else {
|
||||
w.Write([]byte("]: <\n"))
|
||||
w.ind++
|
||||
}
|
||||
if err := tm.writeStruct(w, m.Elem()); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if w.compact {
|
||||
w.Write([]byte("> "))
|
||||
} else {
|
||||
w.ind--
|
||||
w.Write([]byte(">\n"))
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
||||
if tm.ExpandAny && isAny(sv) {
|
||||
if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
|
||||
return err
|
||||
}
|
||||
}
|
||||
st := sv.Type()
|
||||
sprops := GetProperties(st)
|
||||
for i := 0; i < sv.NumField(); i++ {
|
||||
fv := sv.Field(i)
|
||||
props := sprops.Prop[i]
|
||||
name := st.Field(i).Name
|
||||
|
||||
if strings.HasPrefix(name, "XXX_") {
|
||||
// There are two XXX_ fields:
|
||||
// XXX_unrecognized []byte
|
||||
// XXX_extensions map[int32]proto.Extension
|
||||
// The first is handled here;
|
||||
// the second is handled at the bottom of this function.
|
||||
if name == "XXX_unrecognized" && !fv.IsNil() {
|
||||
if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
||||
// Field not filled in. This could be an optional field or
|
||||
// a required field that wasn't filled in. Either way, there
|
||||
// isn't anything we can show for it.
|
||||
continue
|
||||
}
|
||||
if fv.Kind() == reflect.Slice && fv.IsNil() {
|
||||
// Repeated field that is empty, or a bytes field that is unused.
|
||||
continue
|
||||
}
|
||||
|
||||
if props.Repeated && fv.Kind() == reflect.Slice {
|
||||
// Repeated field.
|
||||
for j := 0; j < fv.Len(); j++ {
|
||||
if err := writeName(w, props); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
v := fv.Index(j)
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
// A nil message in a repeated field is not valid,
|
||||
// but we can handle that more gracefully than panicking.
|
||||
if _, err := w.Write([]byte("<nil>\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := tm.writeAny(w, v, props); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if fv.Kind() == reflect.Map {
|
||||
// Map fields are rendered as a repeated struct with key/value fields.
|
||||
keys := fv.MapKeys()
|
||||
sort.Sort(mapKeys(keys))
|
||||
for _, key := range keys {
|
||||
val := fv.MapIndex(key)
|
||||
if err := writeName(w, props); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// open struct
|
||||
if err := w.WriteByte('<'); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.indent()
|
||||
// key
|
||||
if _, err := w.WriteString("key:"); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
// nil values aren't legal, but we can avoid panicking because of them.
|
||||
if val.Kind() != reflect.Ptr || !val.IsNil() {
|
||||
// value
|
||||
if _, err := w.WriteString("value:"); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tm.writeAny(w, val, props.mvalprop); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// close struct
|
||||
w.unindent()
|
||||
if err := w.WriteByte('>'); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
|
||||
// empty bytes field
|
||||
continue
|
||||
}
|
||||
if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
|
||||
// proto3 non-repeated scalar field; skip if zero value
|
||||
if isProto3Zero(fv) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if fv.Kind() == reflect.Interface {
|
||||
// Check if it is a oneof.
|
||||
if st.Field(i).Tag.Get("protobuf_oneof") != "" {
|
||||
// fv is nil, or holds a pointer to generated struct.
|
||||
// That generated struct has exactly one field,
|
||||
// which has a protobuf struct tag.
|
||||
if fv.IsNil() {
|
||||
continue
|
||||
}
|
||||
inner := fv.Elem().Elem() // interface -> *T -> T
|
||||
tag := inner.Type().Field(0).Tag.Get("protobuf")
|
||||
props = new(Properties) // Overwrite the outer props var, but not its pointee.
|
||||
props.Parse(tag)
|
||||
// Write the value in the oneof, not the oneof itself.
|
||||
fv = inner.Field(0)
|
||||
|
||||
// Special case to cope with malformed messages gracefully:
|
||||
// If the value in the oneof is a nil pointer, don't panic
|
||||
// in writeAny.
|
||||
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
||||
// Use errors.New so writeAny won't render quotes.
|
||||
msg := errors.New("/* nil */")
|
||||
fv = reflect.ValueOf(&msg).Elem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := writeName(w, props); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if b, ok := fv.Interface().(raw); ok {
|
||||
if err := writeRaw(w, b.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Enums have a String method, so writeAny will work fine.
|
||||
if err := tm.writeAny(w, fv, props); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Extensions (the XXX_extensions field).
|
||||
pv := sv.Addr()
|
||||
if pv.Type().Implements(extendableProtoType) {
|
||||
if err := tm.writeExtensions(w, pv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeRaw writes an uninterpreted raw message.
|
||||
func writeRaw(w *textWriter, b []byte) error {
|
||||
if err := w.WriteByte('<'); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.indent()
|
||||
if err := writeUnknownStruct(w, b); err != nil {
|
||||
return err
|
||||
}
|
||||
w.unindent()
|
||||
if err := w.WriteByte('>'); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeAny writes an arbitrary field.
|
||||
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
||||
v = reflect.Indirect(v)
|
||||
|
||||
// Floats have special cases.
|
||||
if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
|
||||
x := v.Float()
|
||||
var b []byte
|
||||
switch {
|
||||
case math.IsInf(x, 1):
|
||||
b = posInf
|
||||
case math.IsInf(x, -1):
|
||||
b = negInf
|
||||
case math.IsNaN(x):
|
||||
b = nan
|
||||
}
|
||||
if b != nil {
|
||||
_, err := w.Write(b)
|
||||
return err
|
||||
}
|
||||
// Other values are handled below.
|
||||
}
|
||||
|
||||
// We don't attempt to serialise every possible value type; only those
|
||||
// that can occur in protocol buffers.
|
||||
switch v.Kind() {
|
||||
case reflect.Slice:
|
||||
// Should only be a []byte; repeated fields are handled in writeStruct.
|
||||
if err := writeString(w, string(v.Interface().([]byte))); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.String:
|
||||
if err := writeString(w, v.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Struct:
|
||||
// Required/optional group/message.
|
||||
var bra, ket byte = '<', '>'
|
||||
if props != nil && props.Wire == "group" {
|
||||
bra, ket = '{', '}'
|
||||
}
|
||||
if err := w.WriteByte(bra); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.indent()
|
||||
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
||||
text, err := etm.MarshalText()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write(text); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := tm.writeStruct(w, v); err != nil {
|
||||
return err
|
||||
}
|
||||
w.unindent()
|
||||
if err := w.WriteByte(ket); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
_, err := fmt.Fprint(w, v.Interface())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// equivalent to C's isprint.
|
||||
func isprint(c byte) bool {
|
||||
return c >= 0x20 && c < 0x7f
|
||||
}
|
||||
|
||||
// writeString writes a string in the protocol buffer text format.
|
||||
// It is similar to strconv.Quote except we don't use Go escape sequences,
|
||||
// we treat the string as a byte sequence, and we use octal escapes.
|
||||
// These differences are to maintain interoperability with the other
|
||||
// languages' implementations of the text format.
|
||||
func writeString(w *textWriter, s string) error {
|
||||
// use WriteByte here to get any needed indent
|
||||
if err := w.WriteByte('"'); err != nil {
|
||||
return err
|
||||
}
|
||||
// Loop over the bytes, not the runes.
|
||||
for i := 0; i < len(s); i++ {
|
||||
var err error
|
||||
// Divergence from C++: we don't escape apostrophes.
|
||||
// There's no need to escape them, and the C++ parser
|
||||
// copes with a naked apostrophe.
|
||||
switch c := s[i]; c {
|
||||
case '\n':
|
||||
_, err = w.w.Write(backslashN)
|
||||
case '\r':
|
||||
_, err = w.w.Write(backslashR)
|
||||
case '\t':
|
||||
_, err = w.w.Write(backslashT)
|
||||
case '"':
|
||||
_, err = w.w.Write(backslashDQ)
|
||||
case '\\':
|
||||
_, err = w.w.Write(backslashBS)
|
||||
default:
|
||||
if isprint(c) {
|
||||
err = w.w.WriteByte(c)
|
||||
} else {
|
||||
_, err = fmt.Fprintf(w.w, "\\%03o", c)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return w.WriteByte('"')
|
||||
}
|
||||
|
||||
func writeUnknownStruct(w *textWriter, data []byte) (err error) {
|
||||
if !w.compact {
|
||||
if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b := NewBuffer(data)
|
||||
for b.index < len(b.buf) {
|
||||
x, err := b.DecodeVarint()
|
||||
if err != nil {
|
||||
_, err := fmt.Fprintf(w, "/* %v */\n", err)
|
||||
return err
|
||||
}
|
||||
wire, tag := x&7, x>>3
|
||||
if wire == WireEndGroup {
|
||||
w.unindent()
|
||||
if _, err := w.Write(endBraceNewline); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, err := fmt.Fprint(w, tag); err != nil {
|
||||
return err
|
||||
}
|
||||
if wire != WireStartGroup {
|
||||
if err := w.WriteByte(':'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !w.compact || wire == WireStartGroup {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
switch wire {
|
||||
case WireBytes:
|
||||
buf, e := b.DecodeRawBytes(false)
|
||||
if e == nil {
|
||||
_, err = fmt.Fprintf(w, "%q", buf)
|
||||
} else {
|
||||
_, err = fmt.Fprintf(w, "/* %v */", e)
|
||||
}
|
||||
case WireFixed32:
|
||||
x, err = b.DecodeFixed32()
|
||||
err = writeUnknownInt(w, x, err)
|
||||
case WireFixed64:
|
||||
x, err = b.DecodeFixed64()
|
||||
err = writeUnknownInt(w, x, err)
|
||||
case WireStartGroup:
|
||||
err = w.WriteByte('{')
|
||||
w.indent()
|
||||
case WireVarint:
|
||||
x, err = b.DecodeVarint()
|
||||
err = writeUnknownInt(w, x, err)
|
||||
default:
|
||||
_, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeUnknownInt(w *textWriter, x uint64, err error) error {
|
||||
if err == nil {
|
||||
_, err = fmt.Fprint(w, x)
|
||||
} else {
|
||||
_, err = fmt.Fprintf(w, "/* %v */", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type int32Slice []int32
|
||||
|
||||
func (s int32Slice) Len() int { return len(s) }
|
||||
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
|
||||
func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// writeExtensions writes all the extensions in pv.
|
||||
// pv is assumed to be a pointer to a protocol message struct that is extendable.
|
||||
func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
|
||||
emap := extensionMaps[pv.Type().Elem()]
|
||||
ep := pv.Interface().(extendableProto)
|
||||
|
||||
// Order the extensions by ID.
|
||||
// This isn't strictly necessary, but it will give us
|
||||
// canonical output, which will also make testing easier.
|
||||
m := ep.ExtensionMap()
|
||||
ids := make([]int32, 0, len(m))
|
||||
for id := range m {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Sort(int32Slice(ids))
|
||||
|
||||
for _, extNum := range ids {
|
||||
ext := m[extNum]
|
||||
var desc *ExtensionDesc
|
||||
if emap != nil {
|
||||
desc = emap[extNum]
|
||||
}
|
||||
if desc == nil {
|
||||
// Unknown extension.
|
||||
if err := writeUnknownStruct(w, ext.enc); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
pb, err := GetExtension(ep, desc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed getting extension: %v", err)
|
||||
}
|
||||
|
||||
// Repeated extensions will appear as a slice.
|
||||
if !desc.repeated() {
|
||||
if err := tm.writeExtension(w, desc.Name, pb); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
v := reflect.ValueOf(pb)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
|
||||
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *textWriter) writeIndent() {
|
||||
if !w.complete {
|
||||
return
|
||||
}
|
||||
remain := w.ind * 2
|
||||
for remain > 0 {
|
||||
n := remain
|
||||
if n > len(spaces) {
|
||||
n = len(spaces)
|
||||
}
|
||||
w.w.Write(spaces[:n])
|
||||
remain -= n
|
||||
}
|
||||
w.complete = false
|
||||
}
|
||||
|
||||
// TextMarshaler is a configurable text format marshaler.
|
||||
type TextMarshaler struct {
|
||||
Compact bool // use compact text format (one line).
|
||||
ExpandAny bool // expand google.protobuf.Any messages of known types
|
||||
}
|
||||
|
||||
// Marshal writes a given protocol buffer in text format.
|
||||
// The only errors returned are from w.
|
||||
func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
||||
val := reflect.ValueOf(pb)
|
||||
if pb == nil || val.IsNil() {
|
||||
w.Write([]byte("<nil>"))
|
||||
return nil
|
||||
}
|
||||
var bw *bufio.Writer
|
||||
ww, ok := w.(writer)
|
||||
if !ok {
|
||||
bw = bufio.NewWriter(w)
|
||||
ww = bw
|
||||
}
|
||||
aw := &textWriter{
|
||||
w: ww,
|
||||
complete: true,
|
||||
compact: tm.Compact,
|
||||
}
|
||||
|
||||
if etm, ok := pb.(encoding.TextMarshaler); ok {
|
||||
text, err := etm.MarshalText()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = aw.Write(text); err != nil {
|
||||
return err
|
||||
}
|
||||
if bw != nil {
|
||||
return bw.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Dereference the received pointer so we don't have outer < and >.
|
||||
v := reflect.Indirect(val)
|
||||
if err := tm.writeStruct(aw, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if bw != nil {
|
||||
return bw.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Text is the same as Marshal, but returns the string directly.
|
||||
func (tm *TextMarshaler) Text(pb Message) string {
|
||||
var buf bytes.Buffer
|
||||
tm.Marshal(&buf, pb)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
var (
|
||||
defaultTextMarshaler = TextMarshaler{}
|
||||
compactTextMarshaler = TextMarshaler{Compact: true}
|
||||
)
|
||||
|
||||
// TODO: consider removing some of the Marshal functions below.
|
||||
|
||||
// MarshalText writes a given protocol buffer in text format.
|
||||
// The only errors returned are from w.
|
||||
func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
|
||||
|
||||
// MarshalTextString is the same as MarshalText, but returns the string directly.
|
||||
func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
|
||||
|
||||
// CompactText writes a given protocol buffer in compact text format (one line).
|
||||
func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
|
||||
|
||||
// CompactTextString is the same as CompactText, but returns the string directly.
|
||||
func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
|
872
Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
872
Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
@ -1,872 +0,0 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
// Functions for parsing the Text protocol buffer format.
|
||||
// TODO: message sets.
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type ParseError struct {
|
||||
Message string
|
||||
Line int // 1-based line number
|
||||
Offset int // 0-based byte offset from start of input
|
||||
}
|
||||
|
||||
func (p *ParseError) Error() string {
|
||||
if p.Line == 1 {
|
||||
// show offset only for first line
|
||||
return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message)
|
||||
}
|
||||
return fmt.Sprintf("line %d: %v", p.Line, p.Message)
|
||||
}
|
||||
|
||||
type token struct {
|
||||
value string
|
||||
err *ParseError
|
||||
line int // line number
|
||||
offset int // byte number from start of input, not start of line
|
||||
unquoted string // the unquoted version of value, if it was a quoted string
|
||||
}
|
||||
|
||||
func (t *token) String() string {
|
||||
if t.err == nil {
|
||||
return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset)
|
||||
}
|
||||
return fmt.Sprintf("parse error: %v", t.err)
|
||||
}
|
||||
|
||||
type textParser struct {
|
||||
s string // remaining input
|
||||
done bool // whether the parsing is finished (success or error)
|
||||
backed bool // whether back() was called
|
||||
offset, line int
|
||||
cur token
|
||||
}
|
||||
|
||||
func newTextParser(s string) *textParser {
|
||||
p := new(textParser)
|
||||
p.s = s
|
||||
p.line = 1
|
||||
p.cur.line = 1
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
|
||||
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
|
||||
p.cur.err = pe
|
||||
p.done = true
|
||||
return pe
|
||||
}
|
||||
|
||||
// Numbers and identifiers are matched by [-+._A-Za-z0-9]
|
||||
func isIdentOrNumberChar(c byte) bool {
|
||||
switch {
|
||||
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
|
||||
return true
|
||||
case '0' <= c && c <= '9':
|
||||
return true
|
||||
}
|
||||
switch c {
|
||||
case '-', '+', '.', '_':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isWhitespace(c byte) bool {
|
||||
switch c {
|
||||
case ' ', '\t', '\n', '\r':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isQuote(c byte) bool {
|
||||
switch c {
|
||||
case '"', '\'':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *textParser) skipWhitespace() {
|
||||
i := 0
|
||||
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
|
||||
if p.s[i] == '#' {
|
||||
// comment; skip to end of line or input
|
||||
for i < len(p.s) && p.s[i] != '\n' {
|
||||
i++
|
||||
}
|
||||
if i == len(p.s) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if p.s[i] == '\n' {
|
||||
p.line++
|
||||
}
|
||||
i++
|
||||
}
|
||||
p.offset += i
|
||||
p.s = p.s[i:len(p.s)]
|
||||
if len(p.s) == 0 {
|
||||
p.done = true
|
||||
}
|
||||
}
|
||||
|
||||
func (p *textParser) advance() {
|
||||
// Skip whitespace
|
||||
p.skipWhitespace()
|
||||
if p.done {
|
||||
return
|
||||
}
|
||||
|
||||
// Start of non-whitespace
|
||||
p.cur.err = nil
|
||||
p.cur.offset, p.cur.line = p.offset, p.line
|
||||
p.cur.unquoted = ""
|
||||
switch p.s[0] {
|
||||
case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
|
||||
// Single symbol
|
||||
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
|
||||
case '"', '\'':
|
||||
// Quoted string
|
||||
i := 1
|
||||
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
|
||||
if p.s[i] == '\\' && i+1 < len(p.s) {
|
||||
// skip escaped char
|
||||
i++
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(p.s) || p.s[i] != p.s[0] {
|
||||
p.errorf("unmatched quote")
|
||||
return
|
||||
}
|
||||
unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
|
||||
if err != nil {
|
||||
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
|
||||
return
|
||||
}
|
||||
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
|
||||
p.cur.unquoted = unq
|
||||
default:
|
||||
i := 0
|
||||
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
|
||||
i++
|
||||
}
|
||||
if i == 0 {
|
||||
p.errorf("unexpected byte %#x", p.s[0])
|
||||
return
|
||||
}
|
||||
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
|
||||
}
|
||||
p.offset += len(p.cur.value)
|
||||
}
|
||||
|
||||
var (
|
||||
errBadUTF8 = errors.New("proto: bad UTF-8")
|
||||
errBadHex = errors.New("proto: bad hexadecimal")
|
||||
)
|
||||
|
||||
func unquoteC(s string, quote rune) (string, error) {
|
||||
// This is based on C++'s tokenizer.cc.
|
||||
// Despite its name, this is *not* parsing C syntax.
|
||||
// For instance, "\0" is an invalid quoted string.
|
||||
|
||||
// Avoid allocation in trivial cases.
|
||||
simple := true
|
||||
for _, r := range s {
|
||||
if r == '\\' || r == quote {
|
||||
simple = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if simple {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
buf := make([]byte, 0, 3*len(s)/2)
|
||||
for len(s) > 0 {
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
if r == utf8.RuneError && n == 1 {
|
||||
return "", errBadUTF8
|
||||
}
|
||||
s = s[n:]
|
||||
if r != '\\' {
|
||||
if r < utf8.RuneSelf {
|
||||
buf = append(buf, byte(r))
|
||||
} else {
|
||||
buf = append(buf, string(r)...)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
ch, tail, err := unescape(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf = append(buf, ch...)
|
||||
s = tail
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
func unescape(s string) (ch string, tail string, err error) {
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
if r == utf8.RuneError && n == 1 {
|
||||
return "", "", errBadUTF8
|
||||
}
|
||||
s = s[n:]
|
||||
switch r {
|
||||
case 'a':
|
||||
return "\a", s, nil
|
||||
case 'b':
|
||||
return "\b", s, nil
|
||||
case 'f':
|
||||
return "\f", s, nil
|
||||
case 'n':
|
||||
return "\n", s, nil
|
||||
case 'r':
|
||||
return "\r", s, nil
|
||||
case 't':
|
||||
return "\t", s, nil
|
||||
case 'v':
|
||||
return "\v", s, nil
|
||||
case '?':
|
||||
return "?", s, nil // trigraph workaround
|
||||
case '\'', '"', '\\':
|
||||
return string(r), s, nil
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', 'x', 'X':
|
||||
if len(s) < 2 {
|
||||
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
|
||||
}
|
||||
base := 8
|
||||
ss := s[:2]
|
||||
s = s[2:]
|
||||
if r == 'x' || r == 'X' {
|
||||
base = 16
|
||||
} else {
|
||||
ss = string(r) + ss
|
||||
}
|
||||
i, err := strconv.ParseUint(ss, base, 8)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return string([]byte{byte(i)}), s, nil
|
||||
case 'u', 'U':
|
||||
n := 4
|
||||
if r == 'U' {
|
||||
n = 8
|
||||
}
|
||||
if len(s) < n {
|
||||
return "", "", fmt.Errorf(`\%c requires %d digits`, r, n)
|
||||
}
|
||||
|
||||
bs := make([]byte, n/2)
|
||||
for i := 0; i < n; i += 2 {
|
||||
a, ok1 := unhex(s[i])
|
||||
b, ok2 := unhex(s[i+1])
|
||||
if !ok1 || !ok2 {
|
||||
return "", "", errBadHex
|
||||
}
|
||||
bs[i/2] = a<<4 | b
|
||||
}
|
||||
s = s[n:]
|
||||
return string(bs), s, nil
|
||||
}
|
||||
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
||||
}
|
||||
|
||||
// Adapted from src/pkg/strconv/quote.go.
|
||||
func unhex(b byte) (v byte, ok bool) {
|
||||
switch {
|
||||
case '0' <= b && b <= '9':
|
||||
return b - '0', true
|
||||
case 'a' <= b && b <= 'f':
|
||||
return b - 'a' + 10, true
|
||||
case 'A' <= b && b <= 'F':
|
||||
return b - 'A' + 10, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Back off the parser by one token. Can only be done between calls to next().
|
||||
// It makes the next advance() a no-op.
|
||||
func (p *textParser) back() { p.backed = true }
|
||||
|
||||
// Advances the parser and returns the new current token.
|
||||
func (p *textParser) next() *token {
|
||||
if p.backed || p.done {
|
||||
p.backed = false
|
||||
return &p.cur
|
||||
}
|
||||
p.advance()
|
||||
if p.done {
|
||||
p.cur.value = ""
|
||||
} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
|
||||
// Look for multiple quoted strings separated by whitespace,
|
||||
// and concatenate them.
|
||||
cat := p.cur
|
||||
for {
|
||||
p.skipWhitespace()
|
||||
if p.done || !isQuote(p.s[0]) {
|
||||
break
|
||||
}
|
||||
p.advance()
|
||||
if p.cur.err != nil {
|
||||
return &p.cur
|
||||
}
|
||||
cat.value += " " + p.cur.value
|
||||
cat.unquoted += p.cur.unquoted
|
||||
}
|
||||
p.done = false // parser may have seen EOF, but we want to return cat
|
||||
p.cur = cat
|
||||
}
|
||||
return &p.cur
|
||||
}
|
||||
|
||||
func (p *textParser) consumeToken(s string) error {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value != s {
|
||||
p.back()
|
||||
return p.errorf("expected %q, found %q", s, tok.value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return a RequiredNotSetError indicating which required field was not set.
|
||||
func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
|
||||
st := sv.Type()
|
||||
sprops := GetProperties(st)
|
||||
for i := 0; i < st.NumField(); i++ {
|
||||
if !isNil(sv.Field(i)) {
|
||||
continue
|
||||
}
|
||||
|
||||
props := sprops.Prop[i]
|
||||
if props.Required {
|
||||
return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)}
|
||||
}
|
||||
}
|
||||
return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen
|
||||
}
|
||||
|
||||
// Returns the index in the struct for the named field, as well as the parsed tag properties.
|
||||
func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) {
|
||||
i, ok := sprops.decoderOrigNames[name]
|
||||
if ok {
|
||||
return i, sprops.Prop[i], true
|
||||
}
|
||||
return -1, nil, false
|
||||
}
|
||||
|
||||
// Consume a ':' from the input stream (if the next token is a colon),
|
||||
// returning an error if a colon is needed but not present.
|
||||
func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value != ":" {
|
||||
// Colon is optional when the field is a group or message.
|
||||
needColon := true
|
||||
switch props.Wire {
|
||||
case "group":
|
||||
needColon = false
|
||||
case "bytes":
|
||||
// A "bytes" field is either a message, a string, or a repeated field;
|
||||
// those three become *T, *string and []T respectively, so we can check for
|
||||
// this field being a pointer to a non-string.
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
// *T or *string
|
||||
if typ.Elem().Kind() == reflect.String {
|
||||
break
|
||||
}
|
||||
} else if typ.Kind() == reflect.Slice {
|
||||
// []T or []*T
|
||||
if typ.Elem().Kind() != reflect.Ptr {
|
||||
break
|
||||
}
|
||||
} else if typ.Kind() == reflect.String {
|
||||
// The proto3 exception is for a string field,
|
||||
// which requires a colon.
|
||||
break
|
||||
}
|
||||
needColon = false
|
||||
}
|
||||
if needColon {
|
||||
return p.errorf("expected ':', found %q", tok.value)
|
||||
}
|
||||
p.back()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
||||
st := sv.Type()
|
||||
sprops := GetProperties(st)
|
||||
reqCount := sprops.reqCount
|
||||
var reqFieldErr error
|
||||
fieldSet := make(map[string]bool)
|
||||
// A struct is a sequence of "name: value", terminated by one of
|
||||
// '>' or '}', or the end of the input. A name may also be
|
||||
// "[extension]" or "[type/url]".
|
||||
//
|
||||
// The whole struct can also be an expanded Any message, like:
|
||||
// [type/url] < ... struct contents ... >
|
||||
for {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value == terminator {
|
||||
break
|
||||
}
|
||||
if tok.value == "[" {
|
||||
// Looks like an extension or an Any.
|
||||
//
|
||||
// TODO: Check whether we need to handle
|
||||
// namespace rooted names (e.g. ".something.Foo").
|
||||
extName, err := p.consumeExtName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s := strings.LastIndex(extName, "/"); s >= 0 {
|
||||
// If it contains a slash, it's an Any type URL.
|
||||
messageName := extName[s+1:]
|
||||
mt := MessageType(messageName)
|
||||
if mt == nil {
|
||||
return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
|
||||
}
|
||||
tok = p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
// consume an optional colon
|
||||
if tok.value == ":" {
|
||||
tok = p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
}
|
||||
var terminator string
|
||||
switch tok.value {
|
||||
case "<":
|
||||
terminator = ">"
|
||||
case "{":
|
||||
terminator = "}"
|
||||
default:
|
||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
||||
}
|
||||
v := reflect.New(mt.Elem())
|
||||
if pe := p.readStruct(v.Elem(), terminator); pe != nil {
|
||||
return pe
|
||||
}
|
||||
b, err := Marshal(v.Interface().(Message))
|
||||
if err != nil {
|
||||
return p.errorf("failed to marshal message of type %q: %v", messageName, err)
|
||||
}
|
||||
sv.FieldByName("TypeUrl").SetString(extName)
|
||||
sv.FieldByName("Value").SetBytes(b)
|
||||
continue
|
||||
}
|
||||
|
||||
var desc *ExtensionDesc
|
||||
// This could be faster, but it's functional.
|
||||
// TODO: Do something smarter than a linear scan.
|
||||
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
|
||||
if d.Name == extName {
|
||||
desc = d
|
||||
break
|
||||
}
|
||||
}
|
||||
if desc == nil {
|
||||
return p.errorf("unrecognized extension %q", extName)
|
||||
}
|
||||
|
||||
props := &Properties{}
|
||||
props.Parse(desc.Tag)
|
||||
|
||||
typ := reflect.TypeOf(desc.ExtensionType)
|
||||
if err := p.checkForColon(props, typ); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rep := desc.repeated()
|
||||
|
||||
// Read the extension structure, and set it in
|
||||
// the value we're constructing.
|
||||
var ext reflect.Value
|
||||
if !rep {
|
||||
ext = reflect.New(typ).Elem()
|
||||
} else {
|
||||
ext = reflect.New(typ.Elem()).Elem()
|
||||
}
|
||||
if err := p.readAny(ext, props); err != nil {
|
||||
if _, ok := err.(*RequiredNotSetError); !ok {
|
||||
return err
|
||||
}
|
||||
reqFieldErr = err
|
||||
}
|
||||
ep := sv.Addr().Interface().(extendableProto)
|
||||
if !rep {
|
||||
SetExtension(ep, desc, ext.Interface())
|
||||
} else {
|
||||
old, err := GetExtension(ep, desc)
|
||||
var sl reflect.Value
|
||||
if err == nil {
|
||||
sl = reflect.ValueOf(old) // existing slice
|
||||
} else {
|
||||
sl = reflect.MakeSlice(typ, 0, 1)
|
||||
}
|
||||
sl = reflect.Append(sl, ext)
|
||||
SetExtension(ep, desc, sl.Interface())
|
||||
}
|
||||
if err := p.consumeOptionalSeparator(); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a normal, non-extension field.
|
||||
name := tok.value
|
||||
var dst reflect.Value
|
||||
fi, props, ok := structFieldByName(sprops, name)
|
||||
if ok {
|
||||
dst = sv.Field(fi)
|
||||
} else if oop, ok := sprops.OneofTypes[name]; ok {
|
||||
// It is a oneof.
|
||||
props = oop.Prop
|
||||
nv := reflect.New(oop.Type.Elem())
|
||||
dst = nv.Elem().Field(0)
|
||||
sv.Field(oop.Field).Set(nv)
|
||||
}
|
||||
if !dst.IsValid() {
|
||||
return p.errorf("unknown field name %q in %v", name, st)
|
||||
}
|
||||
|
||||
if dst.Kind() == reflect.Map {
|
||||
// Consume any colon.
|
||||
if err := p.checkForColon(props, dst.Type()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Construct the map if it doesn't already exist.
|
||||
if dst.IsNil() {
|
||||
dst.Set(reflect.MakeMap(dst.Type()))
|
||||
}
|
||||
key := reflect.New(dst.Type().Key()).Elem()
|
||||
val := reflect.New(dst.Type().Elem()).Elem()
|
||||
|
||||
// The map entry should be this sequence of tokens:
|
||||
// < key : KEY value : VALUE >
|
||||
// Technically the "key" and "value" could come in any order,
|
||||
// but in practice they won't.
|
||||
|
||||
tok := p.next()
|
||||
var terminator string
|
||||
switch tok.value {
|
||||
case "<":
|
||||
terminator = ">"
|
||||
case "{":
|
||||
terminator = "}"
|
||||
default:
|
||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
||||
}
|
||||
if err := p.consumeToken("key"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeToken(":"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.readAny(key, props.mkeyprop); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeOptionalSeparator(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeToken("value"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.readAny(val, props.mvalprop); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeOptionalSeparator(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeToken(terminator); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.SetMapIndex(key, val)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check that it's not already set if it's not a repeated field.
|
||||
if !props.Repeated && fieldSet[name] {
|
||||
return p.errorf("non-repeated field %q was repeated", name)
|
||||
}
|
||||
|
||||
if err := p.checkForColon(props, dst.Type()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse into the field.
|
||||
fieldSet[name] = true
|
||||
if err := p.readAny(dst, props); err != nil {
|
||||
if _, ok := err.(*RequiredNotSetError); !ok {
|
||||
return err
|
||||
}
|
||||
reqFieldErr = err
|
||||
}
|
||||
if props.Required {
|
||||
reqCount--
|
||||
}
|
||||
|
||||
if err := p.consumeOptionalSeparator(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if reqCount > 0 {
|
||||
return p.missingRequiredFieldError(sv)
|
||||
}
|
||||
return reqFieldErr
|
||||
}
|
||||
|
||||
// consumeExtName consumes extension name or expanded Any type URL and the
|
||||
// following ']'. It returns the name or URL consumed.
|
||||
func (p *textParser) consumeExtName() (string, error) {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return "", tok.err
|
||||
}
|
||||
|
||||
// If extension name or type url is quoted, it's a single token.
|
||||
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
|
||||
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return name, p.consumeToken("]")
|
||||
}
|
||||
|
||||
// Consume everything up to "]"
|
||||
var parts []string
|
||||
for tok.value != "]" {
|
||||
parts = append(parts, tok.value)
|
||||
tok = p.next()
|
||||
if tok.err != nil {
|
||||
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, ""), nil
|
||||
}
|
||||
|
||||
// consumeOptionalSeparator consumes an optional semicolon or comma.
|
||||
// It is used in readStruct to provide backward compatibility.
|
||||
func (p *textParser) consumeOptionalSeparator() error {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value != ";" && tok.value != "," {
|
||||
p.back()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *textParser) readAny(v reflect.Value, props *Properties) error {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value == "" {
|
||||
return p.errorf("unexpected EOF")
|
||||
}
|
||||
|
||||
switch fv := v; fv.Kind() {
|
||||
case reflect.Slice:
|
||||
at := v.Type()
|
||||
if at.Elem().Kind() == reflect.Uint8 {
|
||||
// Special case for []byte
|
||||
if tok.value[0] != '"' && tok.value[0] != '\'' {
|
||||
// Deliberately written out here, as the error after
|
||||
// this switch statement would write "invalid []byte: ...",
|
||||
// which is not as user-friendly.
|
||||
return p.errorf("invalid string: %v", tok.value)
|
||||
}
|
||||
bytes := []byte(tok.unquoted)
|
||||
fv.Set(reflect.ValueOf(bytes))
|
||||
return nil
|
||||
}
|
||||
// Repeated field.
|
||||
if tok.value == "[" {
|
||||
// Repeated field with list notation, like [1,2,3].
|
||||
for {
|
||||
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
|
||||
err := p.readAny(fv.Index(fv.Len()-1), props)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value == "]" {
|
||||
break
|
||||
}
|
||||
if tok.value != "," {
|
||||
return p.errorf("Expected ']' or ',' found %q", tok.value)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// One value of the repeated field.
|
||||
p.back()
|
||||
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
|
||||
return p.readAny(fv.Index(fv.Len()-1), props)
|
||||
case reflect.Bool:
|
||||
// Either "true", "false", 1 or 0.
|
||||
switch tok.value {
|
||||
case "true", "1":
|
||||
fv.SetBool(true)
|
||||
return nil
|
||||
case "false", "0":
|
||||
fv.SetBool(false)
|
||||
return nil
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
v := tok.value
|
||||
// Ignore 'f' for compatibility with output generated by C++, but don't
|
||||
// remove 'f' when the value is "-inf" or "inf".
|
||||
if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" {
|
||||
v = v[:len(v)-1]
|
||||
}
|
||||
if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil {
|
||||
fv.SetFloat(f)
|
||||
return nil
|
||||
}
|
||||
case reflect.Int32:
|
||||
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
|
||||
fv.SetInt(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(props.Enum) == 0 {
|
||||
break
|
||||
}
|
||||
m, ok := enumValueMaps[props.Enum]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
x, ok := m[tok.value]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
fv.SetInt(int64(x))
|
||||
return nil
|
||||
case reflect.Int64:
|
||||
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
|
||||
fv.SetInt(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// A basic field (indirected through pointer), or a repeated message/group
|
||||
p.back()
|
||||
fv.Set(reflect.New(fv.Type().Elem()))
|
||||
return p.readAny(fv.Elem(), props)
|
||||
case reflect.String:
|
||||
if tok.value[0] == '"' || tok.value[0] == '\'' {
|
||||
fv.SetString(tok.unquoted)
|
||||
return nil
|
||||
}
|
||||
case reflect.Struct:
|
||||
var terminator string
|
||||
switch tok.value {
|
||||
case "{":
|
||||
terminator = "}"
|
||||
case "<":
|
||||
terminator = ">"
|
||||
default:
|
||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
||||
}
|
||||
// TODO: Handle nested messages which implement encoding.TextUnmarshaler.
|
||||
return p.readStruct(fv, terminator)
|
||||
case reflect.Uint32:
|
||||
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
|
||||
fv.SetUint(uint64(x))
|
||||
return nil
|
||||
}
|
||||
case reflect.Uint64:
|
||||
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
|
||||
fv.SetUint(x)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return p.errorf("invalid %v: %v", v.Type(), tok.value)
|
||||
}
|
||||
|
||||
// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb
|
||||
// before starting to unmarshal, so any existing data in pb is always removed.
|
||||
// If a required field is not set and no other error occurs,
|
||||
// UnmarshalText returns *RequiredNotSetError.
|
||||
func UnmarshalText(s string, pb Message) error {
|
||||
if um, ok := pb.(encoding.TextUnmarshaler); ok {
|
||||
err := um.UnmarshalText([]byte(s))
|
||||
return err
|
||||
}
|
||||
pb.Reset()
|
||||
v := reflect.ValueOf(pb)
|
||||
if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil {
|
||||
return pe
|
||||
}
|
||||
return nil
|
||||
}
|
6
Godeps/_workspace/src/github.com/manucorporat/sse/.travis.yml
generated
vendored
6
Godeps/_workspace/src/github.com/manucorporat/sse/.travis.yml
generated
vendored
@ -1,6 +0,0 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
- tip
|
21
Godeps/_workspace/src/github.com/manucorporat/sse/LICENSE
generated
vendored
21
Godeps/_workspace/src/github.com/manucorporat/sse/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Manuel Martínez-Almeida
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
54
Godeps/_workspace/src/github.com/manucorporat/sse/README.md
generated
vendored
54
Godeps/_workspace/src/github.com/manucorporat/sse/README.md
generated
vendored
@ -1,54 +0,0 @@
|
||||
#Server-Sent Events [](https://godoc.org/github.com/manucorporat/sse) [](https://travis-ci.org/manucorporat/sse)
|
||||
|
||||
Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is [standardized as part of HTML5[1] by the W3C](http://www.w3.org/TR/2009/WD-eventsource-20091029/).
|
||||
|
||||
- [Real world demostration using Gin](http://sse.getgin.io/)
|
||||
- [Read this great SSE introduction by the HTML5Rocks guys](http://www.html5rocks.com/en/tutorials/eventsource/basics/)
|
||||
- [Browser support](http://caniuse.com/#feat=eventsource)
|
||||
|
||||
##Sample code
|
||||
|
||||
```go
|
||||
import "github.com/manucorporat/sse"
|
||||
|
||||
func httpHandler(w http.ResponseWriter, req *http.Request) {
|
||||
// data can be a primitive like a string, an integer or a float
|
||||
sse.Encode(w, sse.Event{
|
||||
Event: "message",
|
||||
Data: "some data\nmore data",
|
||||
})
|
||||
|
||||
// also a complex type, like a map, a struct or a slice
|
||||
sse.Encode(w, sse.Event{
|
||||
Id: "124",
|
||||
Event: "message",
|
||||
Data: map[string]interface{}{
|
||||
"user": "manu",
|
||||
"date": time.Now().Unix(),
|
||||
"content": "hi!",
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
```
|
||||
event: message
|
||||
data: some data\\nmore data
|
||||
|
||||
id: 124
|
||||
event: message
|
||||
data: {"content":"hi!","date":1431540810,"user":"manu"}
|
||||
|
||||
```
|
||||
|
||||
##Content-Type
|
||||
|
||||
```go
|
||||
fmt.Println(sse.ContentType)
|
||||
```
|
||||
```
|
||||
text/event-stream
|
||||
```
|
||||
|
||||
##Decoding support
|
||||
|
||||
There is a client-side implementation of SSE coming soon.
|
116
Godeps/_workspace/src/github.com/manucorporat/sse/sse-decoder.go
generated
vendored
116
Godeps/_workspace/src/github.com/manucorporat/sse/sse-decoder.go
generated
vendored
@ -1,116 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type decoder struct {
|
||||
events []Event
|
||||
}
|
||||
|
||||
func Decode(r io.Reader) ([]Event, error) {
|
||||
var dec decoder
|
||||
return dec.decode(r)
|
||||
}
|
||||
|
||||
func (d *decoder) dispatchEvent(event Event, data string) {
|
||||
dataLength := len(data)
|
||||
if dataLength > 0 {
|
||||
//If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
|
||||
data = data[:dataLength-1]
|
||||
dataLength--
|
||||
}
|
||||
if dataLength == 0 && event.Event == "" {
|
||||
return
|
||||
}
|
||||
if event.Event == "" {
|
||||
event.Event = "message"
|
||||
}
|
||||
event.Data = data
|
||||
d.events = append(d.events, event)
|
||||
}
|
||||
|
||||
func (d *decoder) decode(r io.Reader) ([]Event, error) {
|
||||
buf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var currentEvent Event
|
||||
var dataBuffer *bytes.Buffer = new(bytes.Buffer)
|
||||
// TODO (and unit tests)
|
||||
// Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair,
|
||||
// a single U+000A LINE FEED (LF) character,
|
||||
// or a single U+000D CARRIAGE RETURN (CR) character.
|
||||
lines := bytes.Split(buf, []byte{'\n'})
|
||||
for _, line := range lines {
|
||||
if len(line) == 0 {
|
||||
// If the line is empty (a blank line). Dispatch the event.
|
||||
d.dispatchEvent(currentEvent, dataBuffer.String())
|
||||
|
||||
// reset current event and data buffer
|
||||
currentEvent = Event{}
|
||||
dataBuffer.Reset()
|
||||
continue
|
||||
}
|
||||
if line[0] == byte(':') {
|
||||
// If the line starts with a U+003A COLON character (:), ignore the line.
|
||||
continue
|
||||
}
|
||||
|
||||
var field, value []byte
|
||||
colonIndex := bytes.IndexRune(line, ':')
|
||||
if colonIndex != -1 {
|
||||
// If the line contains a U+003A COLON character character (:)
|
||||
// Collect the characters on the line before the first U+003A COLON character (:),
|
||||
// and let field be that string.
|
||||
field = line[:colonIndex]
|
||||
// Collect the characters on the line after the first U+003A COLON character (:),
|
||||
// and let value be that string.
|
||||
value = line[colonIndex+1:]
|
||||
// If value starts with a single U+0020 SPACE character, remove it from value.
|
||||
if len(value) > 0 && value[0] == ' ' {
|
||||
value = value[1:]
|
||||
}
|
||||
} else {
|
||||
// Otherwise, the string is not empty but does not contain a U+003A COLON character character (:)
|
||||
// Use the whole line as the field name, and the empty string as the field value.
|
||||
field = line
|
||||
value = []byte{}
|
||||
}
|
||||
// The steps to process the field given a field name and a field value depend on the field name,
|
||||
// as given in the following list. Field names must be compared literally,
|
||||
// with no case folding performed.
|
||||
switch string(field) {
|
||||
case "event":
|
||||
// Set the event name buffer to field value.
|
||||
currentEvent.Event = string(value)
|
||||
case "id":
|
||||
// Set the event stream's last event ID to the field value.
|
||||
currentEvent.Id = string(value)
|
||||
case "retry":
|
||||
// If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
|
||||
// then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer.
|
||||
// Otherwise, ignore the field.
|
||||
currentEvent.Id = string(value)
|
||||
case "data":
|
||||
// Append the field value to the data buffer,
|
||||
dataBuffer.Write(value)
|
||||
// then append a single U+000A LINE FEED (LF) character to the data buffer.
|
||||
dataBuffer.WriteString("\n")
|
||||
default:
|
||||
//Otherwise. The field is ignored.
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Once the end of the file is reached, the user agent must dispatch the event one final time.
|
||||
d.dispatchEvent(currentEvent, dataBuffer.String())
|
||||
|
||||
return d.events, nil
|
||||
}
|
106
Godeps/_workspace/src/github.com/manucorporat/sse/sse-encoder.go
generated
vendored
106
Godeps/_workspace/src/github.com/manucorporat/sse/sse-encoder.go
generated
vendored
@ -1,106 +0,0 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sse
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Server-Sent Events
|
||||
// W3C Working Draft 29 October 2009
|
||||
// http://www.w3.org/TR/2009/WD-eventsource-20091029/
|
||||
|
||||
const ContentType = "text/event-stream"
|
||||
|
||||
var contentType = []string{ContentType}
|
||||
var noCache = []string{"no-cache"}
|
||||
|
||||
var fieldReplacer = strings.NewReplacer(
|
||||
"\n", "\\n",
|
||||
"\r", "\\r")
|
||||
|
||||
var dataReplacer = strings.NewReplacer(
|
||||
"\n", "\ndata:",
|
||||
"\r", "\\r")
|
||||
|
||||
type Event struct {
|
||||
Event string
|
||||
Id string
|
||||
Retry uint
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func Encode(writer io.Writer, event Event) error {
|
||||
w := checkWriter(writer)
|
||||
writeId(w, event.Id)
|
||||
writeEvent(w, event.Event)
|
||||
writeRetry(w, event.Retry)
|
||||
return writeData(w, event.Data)
|
||||
}
|
||||
|
||||
func writeId(w stringWriter, id string) {
|
||||
if len(id) > 0 {
|
||||
w.WriteString("id:")
|
||||
fieldReplacer.WriteString(w, id)
|
||||
w.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func writeEvent(w stringWriter, event string) {
|
||||
if len(event) > 0 {
|
||||
w.WriteString("event:")
|
||||
fieldReplacer.WriteString(w, event)
|
||||
w.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func writeRetry(w stringWriter, retry uint) {
|
||||
if retry > 0 {
|
||||
w.WriteString("retry:")
|
||||
w.WriteString(strconv.FormatUint(uint64(retry), 10))
|
||||
w.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func writeData(w stringWriter, data interface{}) error {
|
||||
w.WriteString("data:")
|
||||
switch kindOfData(data) {
|
||||
case reflect.Struct, reflect.Slice, reflect.Map:
|
||||
err := json.NewEncoder(w).Encode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteString("\n")
|
||||
default:
|
||||
dataReplacer.WriteString(w, fmt.Sprint(data))
|
||||
w.WriteString("\n\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Event) Render(w http.ResponseWriter) error {
|
||||
header := w.Header()
|
||||
header["Content-Type"] = contentType
|
||||
|
||||
if _, exist := header["Cache-Control"]; !exist {
|
||||
header["Cache-Control"] = noCache
|
||||
}
|
||||
return Encode(w, r)
|
||||
}
|
||||
|
||||
func kindOfData(data interface{}) reflect.Kind {
|
||||
value := reflect.ValueOf(data)
|
||||
valueType := value.Kind()
|
||||
if valueType == reflect.Ptr {
|
||||
valueType = value.Elem().Kind()
|
||||
}
|
||||
return valueType
|
||||
}
|
24
Godeps/_workspace/src/github.com/manucorporat/sse/writer.go
generated
vendored
24
Godeps/_workspace/src/github.com/manucorporat/sse/writer.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
package sse
|
||||
|
||||
import "io"
|
||||
|
||||
type stringWriter interface {
|
||||
io.Writer
|
||||
WriteString(string) (int, error)
|
||||
}
|
||||
|
||||
type stringWrapper struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (w stringWrapper) WriteString(str string) (int, error) {
|
||||
return w.Writer.Write([]byte(str))
|
||||
}
|
||||
|
||||
func checkWriter(writer io.Writer) stringWriter {
|
||||
if w, ok := writer.(stringWriter); ok {
|
||||
return w
|
||||
} else {
|
||||
return stringWrapper{writer}
|
||||
}
|
||||
}
|
14
Godeps/_workspace/src/github.com/vaughan0/go-ini/LICENSE
generated
vendored
14
Godeps/_workspace/src/github.com/vaughan0/go-ini/LICENSE
generated
vendored
@ -1,14 +0,0 @@
|
||||
Copyright (c) 2013 Vaughan Newton
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
70
Godeps/_workspace/src/github.com/vaughan0/go-ini/README.md
generated
vendored
70
Godeps/_workspace/src/github.com/vaughan0/go-ini/README.md
generated
vendored
@ -1,70 +0,0 @@
|
||||
go-ini
|
||||
======
|
||||
|
||||
INI parsing library for Go (golang).
|
||||
|
||||
View the API documentation [here](http://godoc.org/github.com/vaughan0/go-ini).
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Parse an INI file:
|
||||
|
||||
```go
|
||||
import "github.com/vaughan0/go-ini"
|
||||
|
||||
file, err := ini.LoadFile("myfile.ini")
|
||||
```
|
||||
|
||||
Get data from the parsed file:
|
||||
|
||||
```go
|
||||
name, ok := file.Get("person", "name")
|
||||
if !ok {
|
||||
panic("'name' variable missing from 'person' section")
|
||||
}
|
||||
```
|
||||
|
||||
Iterate through values in a section:
|
||||
|
||||
```go
|
||||
for key, value := range file["mysection"] {
|
||||
fmt.Printf("%s => %s\n", key, value)
|
||||
}
|
||||
```
|
||||
|
||||
Iterate through sections in a file:
|
||||
|
||||
```go
|
||||
for name, section := range file {
|
||||
fmt.Printf("Section name: %s\n", name)
|
||||
}
|
||||
```
|
||||
|
||||
File Format
|
||||
-----------
|
||||
|
||||
INI files are parsed by go-ini line-by-line. Each line may be one of the following:
|
||||
|
||||
* A section definition: [section-name]
|
||||
* A property: key = value
|
||||
* A comment: #blahblah _or_ ;blahblah
|
||||
* Blank. The line will be ignored.
|
||||
|
||||
Properties defined before any section headers are placed in the default section, which has
|
||||
the empty string as it's key.
|
||||
|
||||
Example:
|
||||
|
||||
```ini
|
||||
# I am a comment
|
||||
; So am I!
|
||||
|
||||
[apples]
|
||||
colour = red or green
|
||||
shape = applish
|
||||
|
||||
[oranges]
|
||||
shape = square
|
||||
colour = blue
|
||||
```
|
123
Godeps/_workspace/src/github.com/vaughan0/go-ini/ini.go
generated
vendored
123
Godeps/_workspace/src/github.com/vaughan0/go-ini/ini.go
generated
vendored
@ -1,123 +0,0 @@
|
||||
// Package ini provides functions for parsing INI configuration files.
|
||||
package ini
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
sectionRegex = regexp.MustCompile(`^\[(.*)\]$`)
|
||||
assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
|
||||
)
|
||||
|
||||
// ErrSyntax is returned when there is a syntax error in an INI file.
|
||||
type ErrSyntax struct {
|
||||
Line int
|
||||
Source string // The contents of the erroneous line, without leading or trailing whitespace
|
||||
}
|
||||
|
||||
func (e ErrSyntax) Error() string {
|
||||
return fmt.Sprintf("invalid INI syntax on line %d: %s", e.Line, e.Source)
|
||||
}
|
||||
|
||||
// A File represents a parsed INI file.
|
||||
type File map[string]Section
|
||||
|
||||
// A Section represents a single section of an INI file.
|
||||
type Section map[string]string
|
||||
|
||||
// Returns a named Section. A Section will be created if one does not already exist for the given name.
|
||||
func (f File) Section(name string) Section {
|
||||
section := f[name]
|
||||
if section == nil {
|
||||
section = make(Section)
|
||||
f[name] = section
|
||||
}
|
||||
return section
|
||||
}
|
||||
|
||||
// Looks up a value for a key in a section and returns that value, along with a boolean result similar to a map lookup.
|
||||
func (f File) Get(section, key string) (value string, ok bool) {
|
||||
if s := f[section]; s != nil {
|
||||
value, ok = s[key]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Loads INI data from a reader and stores the data in the File.
|
||||
func (f File) Load(in io.Reader) (err error) {
|
||||
bufin, ok := in.(*bufio.Reader)
|
||||
if !ok {
|
||||
bufin = bufio.NewReader(in)
|
||||
}
|
||||
return parseFile(bufin, f)
|
||||
}
|
||||
|
||||
// Loads INI data from a named file and stores the data in the File.
|
||||
func (f File) LoadFile(file string) (err error) {
|
||||
in, err := os.Open(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
return f.Load(in)
|
||||
}
|
||||
|
||||
func parseFile(in *bufio.Reader, file File) (err error) {
|
||||
section := ""
|
||||
lineNum := 0
|
||||
for done := false; !done; {
|
||||
var line string
|
||||
if line, err = in.ReadString('\n'); err != nil {
|
||||
if err == io.EOF {
|
||||
done = true
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
lineNum++
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
// Skip blank lines
|
||||
continue
|
||||
}
|
||||
if line[0] == ';' || line[0] == '#' {
|
||||
// Skip comments
|
||||
continue
|
||||
}
|
||||
|
||||
if groups := assignRegex.FindStringSubmatch(line); groups != nil {
|
||||
key, val := groups[1], groups[2]
|
||||
key, val = strings.TrimSpace(key), strings.TrimSpace(val)
|
||||
file.Section(section)[key] = val
|
||||
} else if groups := sectionRegex.FindStringSubmatch(line); groups != nil {
|
||||
name := strings.TrimSpace(groups[1])
|
||||
section = name
|
||||
// Create the section if it does not exist
|
||||
file.Section(section)
|
||||
} else {
|
||||
return ErrSyntax{lineNum, line}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loads and returns a File from a reader.
|
||||
func Load(in io.Reader) (File, error) {
|
||||
file := make(File)
|
||||
err := file.Load(in)
|
||||
return file, err
|
||||
}
|
||||
|
||||
// Loads and returns an INI File from a file on disk.
|
||||
func LoadFile(filename string) (File, error) {
|
||||
file := make(File)
|
||||
err := file.LoadFile(filename)
|
||||
return file, err
|
||||
}
|
2
Godeps/_workspace/src/github.com/vaughan0/go-ini/test.ini
generated
vendored
2
Godeps/_workspace/src/github.com/vaughan0/go-ini/test.ini
generated
vendored
@ -1,2 +0,0 @@
|
||||
[default]
|
||||
stuff = things
|
27
Godeps/_workspace/src/golang.org/x/net/LICENSE
generated
vendored
27
Godeps/_workspace/src/golang.org/x/net/LICENSE
generated
vendored
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
Godeps/_workspace/src/golang.org/x/net/PATENTS
generated
vendored
22
Godeps/_workspace/src/golang.org/x/net/PATENTS
generated
vendored
@ -1,22 +0,0 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
156
Godeps/_workspace/src/golang.org/x/net/context/context.go
generated
vendored
156
Godeps/_workspace/src/golang.org/x/net/context/context.go
generated
vendored
@ -1,156 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package context defines the Context type, which carries deadlines,
|
||||
// cancelation signals, and other request-scoped values across API boundaries
|
||||
// and between processes.
|
||||
//
|
||||
// Incoming requests to a server should create a Context, and outgoing calls to
|
||||
// servers should accept a Context. The chain of function calls between must
|
||||
// propagate the Context, optionally replacing it with a modified copy created
|
||||
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
|
||||
//
|
||||
// Programs that use Contexts should follow these rules to keep interfaces
|
||||
// consistent across packages and enable static analysis tools to check context
|
||||
// propagation:
|
||||
//
|
||||
// Do not store Contexts inside a struct type; instead, pass a Context
|
||||
// explicitly to each function that needs it. The Context should be the first
|
||||
// parameter, typically named ctx:
|
||||
//
|
||||
// func DoSomething(ctx context.Context, arg Arg) error {
|
||||
// // ... use ctx ...
|
||||
// }
|
||||
//
|
||||
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
|
||||
// if you are unsure about which Context to use.
|
||||
//
|
||||
// Use context Values only for request-scoped data that transits processes and
|
||||
// APIs, not for passing optional parameters to functions.
|
||||
//
|
||||
// The same Context may be passed to functions running in different goroutines;
|
||||
// Contexts are safe for simultaneous use by multiple goroutines.
|
||||
//
|
||||
// See http://blog.golang.org/context for example code for a server that uses
|
||||
// Contexts.
|
||||
package context
|
||||
|
||||
import "time"
|
||||
|
||||
// A Context carries a deadline, a cancelation signal, and other values across
|
||||
// API boundaries.
|
||||
//
|
||||
// Context's methods may be called by multiple goroutines simultaneously.
|
||||
type Context interface {
|
||||
// Deadline returns the time when work done on behalf of this context
|
||||
// should be canceled. Deadline returns ok==false when no deadline is
|
||||
// set. Successive calls to Deadline return the same results.
|
||||
Deadline() (deadline time.Time, ok bool)
|
||||
|
||||
// Done returns a channel that's closed when work done on behalf of this
|
||||
// context should be canceled. Done may return nil if this context can
|
||||
// never be canceled. Successive calls to Done return the same value.
|
||||
//
|
||||
// WithCancel arranges for Done to be closed when cancel is called;
|
||||
// WithDeadline arranges for Done to be closed when the deadline
|
||||
// expires; WithTimeout arranges for Done to be closed when the timeout
|
||||
// elapses.
|
||||
//
|
||||
// Done is provided for use in select statements:
|
||||
//
|
||||
// // Stream generates values with DoSomething and sends them to out
|
||||
// // until DoSomething returns an error or ctx.Done is closed.
|
||||
// func Stream(ctx context.Context, out chan<- Value) error {
|
||||
// for {
|
||||
// v, err := DoSomething(ctx)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// return ctx.Err()
|
||||
// case out <- v:
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// See http://blog.golang.org/pipelines for more examples of how to use
|
||||
// a Done channel for cancelation.
|
||||
Done() <-chan struct{}
|
||||
|
||||
// Err returns a non-nil error value after Done is closed. Err returns
|
||||
// Canceled if the context was canceled or DeadlineExceeded if the
|
||||
// context's deadline passed. No other values for Err are defined.
|
||||
// After Done is closed, successive calls to Err return the same value.
|
||||
Err() error
|
||||
|
||||
// Value returns the value associated with this context for key, or nil
|
||||
// if no value is associated with key. Successive calls to Value with
|
||||
// the same key returns the same result.
|
||||
//
|
||||
// Use context values only for request-scoped data that transits
|
||||
// processes and API boundaries, not for passing optional parameters to
|
||||
// functions.
|
||||
//
|
||||
// A key identifies a specific value in a Context. Functions that wish
|
||||
// to store values in Context typically allocate a key in a global
|
||||
// variable then use that key as the argument to context.WithValue and
|
||||
// Context.Value. A key can be any type that supports equality;
|
||||
// packages should define keys as an unexported type to avoid
|
||||
// collisions.
|
||||
//
|
||||
// Packages that define a Context key should provide type-safe accessors
|
||||
// for the values stores using that key:
|
||||
//
|
||||
// // Package user defines a User type that's stored in Contexts.
|
||||
// package user
|
||||
//
|
||||
// import "golang.org/x/net/context"
|
||||
//
|
||||
// // User is the type of value stored in the Contexts.
|
||||
// type User struct {...}
|
||||
//
|
||||
// // key is an unexported type for keys defined in this package.
|
||||
// // This prevents collisions with keys defined in other packages.
|
||||
// type key int
|
||||
//
|
||||
// // userKey is the key for user.User values in Contexts. It is
|
||||
// // unexported; clients use user.NewContext and user.FromContext
|
||||
// // instead of using this key directly.
|
||||
// var userKey key = 0
|
||||
//
|
||||
// // NewContext returns a new Context that carries value u.
|
||||
// func NewContext(ctx context.Context, u *User) context.Context {
|
||||
// return context.WithValue(ctx, userKey, u)
|
||||
// }
|
||||
//
|
||||
// // FromContext returns the User value stored in ctx, if any.
|
||||
// func FromContext(ctx context.Context) (*User, bool) {
|
||||
// u, ok := ctx.Value(userKey).(*User)
|
||||
// return u, ok
|
||||
// }
|
||||
Value(key interface{}) interface{}
|
||||
}
|
||||
|
||||
// Background returns a non-nil, empty Context. It is never canceled, has no
|
||||
// values, and has no deadline. It is typically used by the main function,
|
||||
// initialization, and tests, and as the top-level Context for incoming
|
||||
// requests.
|
||||
func Background() Context {
|
||||
return background
|
||||
}
|
||||
|
||||
// TODO returns a non-nil, empty Context. Code should use context.TODO when
|
||||
// it's unclear which Context to use or it is not yet available (because the
|
||||
// surrounding function has not yet been extended to accept a Context
|
||||
// parameter). TODO is recognized by static analysis tools that determine
|
||||
// whether Contexts are propagated correctly in a program.
|
||||
func TODO() Context {
|
||||
return todo
|
||||
}
|
||||
|
||||
// A CancelFunc tells an operation to abandon its work.
|
||||
// A CancelFunc does not wait for the work to stop.
|
||||
// After the first call, subsequent calls to a CancelFunc do nothing.
|
||||
type CancelFunc func()
|
72
Godeps/_workspace/src/golang.org/x/net/context/go17.go
generated
vendored
72
Godeps/_workspace/src/golang.org/x/net/context/go17.go
generated
vendored
@ -1,72 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.7
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"context" // standard library's context, as of Go 1.7
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
todo = context.TODO()
|
||||
background = context.Background()
|
||||
)
|
||||
|
||||
// Canceled is the error returned by Context.Err when the context is canceled.
|
||||
var Canceled = context.Canceled
|
||||
|
||||
// DeadlineExceeded is the error returned by Context.Err when the context's
|
||||
// deadline passes.
|
||||
var DeadlineExceeded = context.DeadlineExceeded
|
||||
|
||||
// WithCancel returns a copy of parent with a new Done channel. The returned
|
||||
// context's Done channel is closed when the returned cancel function is called
|
||||
// or when the parent context's Done channel is closed, whichever happens first.
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
||||
ctx, f := context.WithCancel(parent)
|
||||
return ctx, CancelFunc(f)
|
||||
}
|
||||
|
||||
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
||||
// to be no later than d. If the parent's deadline is already earlier than d,
|
||||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
||||
// context's Done channel is closed when the deadline expires, when the returned
|
||||
// cancel function is called, or when the parent context's Done channel is
|
||||
// closed, whichever happens first.
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
||||
ctx, f := context.WithDeadline(parent, deadline)
|
||||
return ctx, CancelFunc(f)
|
||||
}
|
||||
|
||||
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete:
|
||||
//
|
||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||
// return slowOperation(ctx)
|
||||
// }
|
||||
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
||||
return WithDeadline(parent, time.Now().Add(timeout))
|
||||
}
|
||||
|
||||
// WithValue returns a copy of parent in which the value associated with key is
|
||||
// val.
|
||||
//
|
||||
// Use context Values only for request-scoped data that transits processes and
|
||||
// APIs, not for passing optional parameters to functions.
|
||||
func WithValue(parent Context, key interface{}, val interface{}) Context {
|
||||
return context.WithValue(parent, key, val)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user