[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
[0409]
[0410]
[0411]
[0412]
[0413]
[0414]
[0415]
[0416]
[0417]
[0418]
[0419]
[0420]
[0421]
[0422]
[0423]
[0424]
[0425]
[0426]
[0427]
[0428]
[0429]
[0430]
[0431]
[0432]
[0433]
[0434]
[0435]
[0436]
[0437]
[0438]
[0439]
[0440]
[0441]
[0442]
[0443]
[0444]
[0445]
[0446]
[0447]
[0448]
[0449]
[0450]
[0451]
[0452]
[0453]
[0454]
[0455]
[0456]
[0457]
[0458]
[0459]
[0460]
[0461]
[0462]
[0463]
[0464]
[0465]
[0466]
[0467]
[0468]
[0469]
[0470]
[0471]
[0472]
[0473]
[0474]
[0475]
[0476]
[0477]
[0478]
[0479]
[0480]
[0481]
[0482]
[0483]
[0484]
[0485]
[0486]
[0487]
[0488]
[0489]
[0490]
[0491]
[0492]
[0493]
[0494]
[0495]
[0496]
[0497]
[0498]
[0499]
[0500]
[0501]
[0502]
[0503]
[0504]
[0505]
[0506]
[0507]
[0508]
[0509]
[0510]
[0511]
[0512]
[0513]
[0514]
[0515]
[0516]
[0517]
[0518]
[0519]
[0520]
[0521]
[0522]
[0523]
[0524]
[0525]
[0526]
[0527]
[0528]
[0529]
[0530]
[0531]
[0532]
[0533]
[0534]
[0535]
[0536]
[0537]
[0538]
[0539]
[0540]
[0541]
[0542]
[0543]
[0544]
[0545]
[0546]
[0547]
[0548]
[0549]
[0550]
[0551]
[0552]
[0553]
[0554]
[0555]
[0556]
[0557]
[0558]
[0559]
[0560]
[0561]
[0562]
[0563]
[0564]
[0565]
[0566]
[0567]
[0568]
[0569]
[0570]
[0571]
[0572]
[0573]
[0574]
[0575]
[0576]
[0577]
[0578]
[0579]
[0580]
[0581]
[0582]
[0583]
[0584]
[0585]
[0586]
[0587]
[0588]
[0589]
[0590]
[0591]
[0592]
[0593]
[0594]
[0595]
[0596]
[0597]
[0598]
[0599]
[0600]
[0601]
[0602]
[0603]
[0604]
[0605]
[0606]
[0607]
[0608]
[0609]
[0610]
[0611]
[0612]
[0613]
[0614]
[0615]
[0616]
[0617]
[0618]
[0619]
[0620]
[0621]
[0622]
[0623]
[0624]
[0625]
[0626]
[0627]
[0628]
[0629]
[0630]
[0631]
[0632]
[0633]
[0634]
[0635]
[0636]
[0637]
[0638]
[0639]
[0640]
[0641]
[0642]
[0643]
[0644]
[0645]
[0646]
[0647]
[0648]
[0649]
[0650]
[0651]
[0652]
[0653]
[0654]
[0655]
[0656]
[0657]
[0658]
[0659]
[0660]
[0661]
[0662]
[0663]
[0664]
[0665]
[0666]
[0667]
[0668]
[0669]
[0670]
[0671]
[0672]
[0673]
[0674]
[0675]
[0676]
[0677]
[0678]
[0679]
[0680]
[0681]
[0682]
[0683]
[0684]
[0685]
[0686]
[0687]
[0688]
[0689]
[0690]
[0691]
[0692]
[0693]
[0694]
[0695]
[0696]
[0697]
[0698]
[0699]
[0700]
[0701]
[0702]
[0703]
[0704]
[0705]
[0706]
[0707]
[0708]
[0709]
[0710]
[0711]
[0712]
[0713]
[0714]
[0715]
[0716]
[0717]
[0718]
[0719]
[0720]
[0721]
[0722]
[0723]
[0724]
[0725]
[0726]
[0727]
[0728]
[0729]
[0730]
[0731]
[0732]
[0733]
[0734]
[0735]
[0736]
[0737]
[0738]
[0739]
[0740]
[0741]
[0742]
[0743]
[0744]
[0745]
[0746]
[0747]
[0748]
[0749]
[0750]
[0751]
[0752]
[0753]
[0754]
[0755]
[0756]
[0757]
[0758]
[0759]
[0760]
[0761]
[0762]
[0763]
[0764]
[0765]
[0766]
[0767]
[0768]
[0769]
[0770]
[0771]
[0772]
[0773]
[0774]
[0775]
[0776]
[0777]
[0778]
[0779]
[0780]
[0781]
[0782]
[0783]
[0784]
[0785]
[0786]
[0787]
[0788]
[0789]
[0790]
[0791]
[0792]
[0793]
[0794]
[0795]
[0796]
[0797]
[0798]
[0799]
[0800]
[0801]
[0802]
[0803]
[0804]
[0805]
[0806]
[0807]
[0808]
[0809]
[0810]
[0811]
[0812]
[0813]
[0814]
[0815]
[0816]
[0817]
[0818]
[0819]
[0820]
[0821]
[0822]
[0823]
[0824]
[0825]
[0826]
[0827]
[0828]
[0829]
[0830]
[0831]
[0832]
[0833]
[0834]
[0835]
[0836]
[0837]
[0838]
[0839]
[0840]
[0841]
[0842]
[0843]
[0844]
[0845]
[0846]
[0847]
[0848]
[0849]
[0850]
[0851]
[0852]
[0853]
[0854]
[0855]
[0856]
[0857]
[0858]
[0859]
[0860]
[0861]
[0862]
[0863]
[0864]
[0865]
[0866]
[0867]
[0868]
[0869]
[0870]
[0871]
[0872]
[0873]
[0874]
[0875]
[0876]
[0877]
[0878]
[0879]
[0880]
[0881]
[0882]
[0883]
[0884]
[0885]
[0886]
[0887]
[0888]
[0889]
[0890]
[0891]
[0892]
[0893]
[0894]
[0895]
[0896]
[0897]
[0898]
[0899]
[0900]
[0901]
[0902]
[0903]
[0904]
[0905]
[0906]
[0907]
[0908]
[0909]
[0910]
[0911]
[0912]
[0913]
[0914]
[0915]
[0916]
[0917]
[0918]
[0919]
[0920]
[0921]
[0922]
[0923]
[0924]
[0925]
[0926]
[0927]
[0928]
[0929]
[0930]
[0931]
[0932]
[0933]
[0934]
[0935]
[0936]
[0937]
[0938]
[0939]
[0940]
[0941]
[0942]
[0943]
[0944]
[0945]
[0946]
[0947]
[0948]
[0949]
[0950]
[0951]
[0952]
[0953]
[0954]
[0955]
[0956]
[0957]
[0958]
[0959]
[0960]
[0961]
[0962]
[0963]
[0964]
[0965]
[0966]
[0967]
[0968]
[0969]
[0970]
[0971]
[0972]
[0973]
[0974]
[0975]
[0976]
[0977]
[0978]
[0979]
[0980]
[0981]
[0982]
[0983]
[0984]
[0985]
[0986]
[0987]
[0988]
[0989]
[0990]
[0991]
[0992]
[0993]
[0994]
[0995]
[0996]
[0997]
[0998]
[0999]
[1000]
[1001]
[1002]
[1003]
[1004]
[1005]
[1006]
[1007]
[1008]
[1009]
[1010]
[1011]
[1012]
[1013]
[1014]
[1015]
[1016]
[1017]
[1018]
[1019]
[1020]
[1021]
[1022]
[1023]
[1024]
[1025]
[1026]
[1027]
[1028]
[1029]
[1030]
[1031]
[1032]
[1033]
[1034]
[1035]
[1036]
[1037]
[1038]
[1039]
[1040]
[1041]
[1042]
[1043]
[1044]
[1045]
[1046]
[1047]
[1048]
[1049]
[1050]
[1051]
[1052]
[1053]
[1054]
[1055]
[1056]
[1057]
[1058]
[1059]
[1060]
[1061]
[1062]
[1063]
[1064]
[1065]
[1066]
[1067]
[1068]
[1069]
[1070]
[1071]
[1072]
[1073]
[1074]
[1075]
[1076]
[1077]
[1078]
[1079]
[1080]
[1081]
[1082]
[1083]
[1084]
[1085]
[1086]
[1087]
[1088]
[1089]
[1090]
[1091]
[1092]
[1093]
[1094]
[1095]
[1096]
[1097]
[1098]
[1099]
[1100]
[1101]
[1102]
[1103]
[1104]
[1105]
[1106]
[1107]
[1108]
[1109]
[1110]
[1111]
[1112]
[1113]
[1114]
[1115]
[1116]
[1117]
[1118]
[1119]
[1120]
[1121]
[1122]
[1123]
[1124]
[1125]
[1126]
[1127]
[1128]
[1129]
[1130]
[1131]
[1132]
[1133]
[1134]
[1135]
[1136]
[1137]
[1138]
[1139]
[1140]
[1141]
[1142]
[1143]
[1144]
[1145]
[1146]
[1147]
[1148]
[1149]
[1150]
[1151]
[1152]
[1153]
[1154]
[1155]
[1156]
[1157]
[1158]
[1159]
[1160]
[1161]
[1162]
[1163]
[1164]
[1165]
[1166]
[1167]
[1168]
[1169]
[1170]
[1171]
[1172]
[1173]
[1174]
[1175]
[1176]
[1177]
[1178]
[1179]
[1180]
[1181]
[1182]
[1183]
[1184]
[1185]
[1186]
[1187]
[1188]
[1189]
[1190]
[1191]
[1192]
[1193]
[1194]
[1195]
[1196]
[1197]
[1198]
[1199]
[1200]
[1201]
[1202]
[1203]
[1204]
[1205]
[1206]
[1207]
[1208]
[1209]
[1210]
[1211]
[1212]
[1213]
[1214]
[1215]
[1216]
[1217]
[1218]
[1219]
[1220]
[1221]
[1222]
[1223]
[1224]
[1225]
[1226]
[1227]
[1228]
[1229]
[1230]
[1231]
[1232]
[1233]
[1234]
[1235]
[1236]
[1237]
[1238]
[1239]
[1240]
[1241]
[1242]
[1243]
[1244]
[1245]
[1246]
[1247]
[1248]
[1249]
[1250]
[1251]
[1252]
[1253]
[1254]
[1255]
[1256]
[1257]
[1258]
[1259]
[1260]
[1261]
[1262]
[1263]
[1264]
[1265]
[1266]
[1267]
[1268]
[1269]
[1270]
[1271]
[1272]
[1273]
[1274]
[1275]
[1276]
[1277]
[1278]
[1279]
[1280]
[1281]
[1282]
[1283]
[1284]
[1285]
[1286]
[1287]
[1288]
[1289]
[1290]
[1291]
[1292]
[1293]
[1294]
[1295]
[1296]
[1297]
[1298]
[1299]
[1300]
[1301]
[1302]
[1303]
[1304]
[1305]
[1306]
[1307]
[1308]
[1309]
[1310]
[1311]
[1312]
[1313]
[1314]
[1315]
[1316]
[1317]
[1318]
[1319]
[1320]
[1321]
[1322]
[1323]
[1324]
[1325]
[1326]
[1327]
[1328]
[1329]
[1330]
[1331]
[1332]
[1333]
[1334]
[1335]
[1336]
[1337]
[1338]
[1339]
[1340]
[1341]
[1342]
[1343]
[1344]
[1345]
[1346]
[1347]
[1348]
[1349]
[1350]
[1351]
[1352]
[1353]
[1354]
[1355]
[1356]
[1357]
[1358]
[1359]
[1360]
[1361]
[1362]
[1363]
[1364]
[1365]
[1366]
[1367]
[1368]
[1369]
[1370]
[1371]
[1372]
[1373]
[1374]
[1375]
[1376]
[1377]
[1378]
[1379]
[1380]
[1381]
[1382]
[1383]
[1384]
[1385]
[1386]
[1387]
[1388]
[1389]
[1390]
[1391]
[1392]
[1393]
[1394]
[1395]
[1396]
[1397]
[1398]
[1399]
[1400]
[1401]
[1402]
[1403]
[1404]
[1405]
[1406]
[1407]
[1408]
[1409]
[1410]
[1411]
[1412]
[1413]
[1414]
[1415]
[1416]
[1417]
[1418]
[1419]
[1420]
[1421]
[1422]
[1423]
[1424]
[1425]
[1426]
[1427]
[1428]
[1429]
[1430]
[1431]
[1432]
[1433]
[1434]
[1435]
[1436]
[1437]
[1438]
[1439]
[1440]
[1441]
[1442]
[1443]
[1444]
[1445]
[1446]
[1447]
[1448]
[1449]
[1450]
[1451]
[1452]
[1453]
[1454]
[1455]
[1456]
[1457]
[1458]
[1459]
[1460]
[1461]
[1462]
[1463]
[1464]
[1465]
[1466]
[1467]
[1468]
[1469]
[1470]
[1471]
[1472]
[1473]
[1474]
[1475]
[1476]
[1477]
[1478]
[1479]
[1480]
[1481]
[1482]
[1483]
[1484]
[1485]
[1486]
[1487]
[1488]
[1489]
[1490]
[1491]
[1492]
[1493]
[1494]
[1495]
[1496]
[1497]
[1498]
[1499]
[1500]
[1501]
[1502]
[1503]
[1504]
[1505]
[1506]
[1507]
[1508]
[1509]
[1510]
[1511]
[1512]
[1513]
[1514]
[1515]
[1516]
[1517]
[1518]
[1519]
[1520]
[1521]
[1522]
[1523]
[1524]
[1525]
[1526]
[1527]
[1528]
[1529]
[1530]
[1531]
[1532]
[1533]
[1534]
[1535]
[1536]
[1537]
[1538]
[1539]
[1540]
[1541]
[1542]
[1543]
[1544]
[1545]
[1546]
[1547]
[1548]
[1549]
[1550]
/*****************************************************************************/
/*
HPACK.c
In HTTP/1.1 header fields are not compressed, the redundant header fields in
these requests unnecessarily consume bandwidth, measurably increasing latency.
HPACK is a compressor that eliminates redundant header fields, limits
vulnerability to known security attacks, and has a bounded memory requirement
for use in constrained environments.
https://tools.ietf.org/html/rfc7541
Note that in section 1.1:
The format defined in this specification treats a list of header
fields as an ordered collection of name-value pairs that can include
duplicate pairs. *Names* and values are considered to be *opaque
sequences of octets*, and the order of header fields is preserved
after being compressed and decompressed.
However value pair names have similar HTTP/1 constraints applied as done by
RequestGet() and RequestFields().
VERSION HISTORY
---------------
19-DEC-2019 MGD apply similar HTTP/1 constraints to (value pair) names
request header fields by RequestGet() and RequestFields()
16-JUN-2017 MGD bugfix; HpackHeadersFrame() use ":authority" pseudo-header
for "Host:" header according to RFC7540 8.1.2.3
06-AUG-2016 MGD bugfix; HpackHeadersFrame() uncompressed header size
22-MAY-2016 MGD bugfix; HpackHeadersFrame() multiple to single cookie header
22-AUG-2015 MGD initial
*/
/*****************************************************************************/
#ifdef WASD_VMS_V7
# undef __VMS_VER
# define __VMS_VER 70000000
# undef __CRTL_VER
# define __CRTL_VER 70000000
#else
# ifdef WASD_VMS_V7
# undef _VMS__V6__SOURCE
# define _VMS__V6__SOURCE
# undef __VMS_VER
# define __VMS_VER 70000000
# undef __CRTL_VER
# define __CRTL_VER 70000000
# endif
#endif
#include <stdio.h>
#include <ctype.h>
#include <inttypes.h>
#include "wasd.h"
#include "hpack_huffman_table.h"
#define WASD_MODULE "HPACK"
/******************/
/* global storage */
/******************/
HPACK_ENTRY_STRUCT HpackStaticTable [HPACK_STATIC_TABLE_COUNT+1] =
{
{ NULL, NULL, 0, 0, 0, NULL, NULL },
{ NULL, NULL, 0, 10, 0, ":authority", NULL },
{ NULL, NULL, 0, 7, 3, ":method", "GET" },
{ NULL, NULL, 0, 7, 4, ":method", "POST" },
{ NULL, NULL, 0, 5, 1, ":path", "/" },
{ NULL, NULL, 0, 5, 11, ":path", "/index.html" },
{ NULL, NULL, 0, 7, 4, ":scheme", "http" },
{ NULL, NULL, 0, 7, 5, ":scheme", "https" },
{ NULL, NULL, 0, 7, 3, ":status", "200" },
{ NULL, NULL, 0, 7, 3, ":status", "204" },
{ NULL, NULL, 0, 7, 3, ":status", "206" },
{ NULL, NULL, 0, 7, 3, ":status", "304" },
{ NULL, NULL, 0, 7, 3, ":status", "400" },
{ NULL, NULL, 0, 7, 3, ":status", "404" },
{ NULL, NULL, 0, 7, 3, ":status", "500" },
{ NULL, NULL, 0, 14, 0, "accept-charset", NULL },
{ NULL, NULL, 0, 15, 13, "accept-encoding", "gzip, deflate" },
{ NULL, NULL, 0, 15, 0, "accept-language", NULL },
{ NULL, NULL, 0, 13, 0, "accept-ranges", NULL },
{ NULL, NULL, 0, 6, 0, "accept", NULL },
{ NULL, NULL, 0, 27, 0, "accept-control-allow-origin", NULL },
{ NULL, NULL, 0, 3, 0, "age", NULL },
{ NULL, NULL, 0, 5, 0, "allow", NULL },
{ NULL, NULL, 0, 13, 0, "authorization", NULL },
{ NULL, NULL, 0, 13, 0, "cache-control", NULL },
{ NULL, NULL, 0, 19, 0, "content-disposition", NULL },
{ NULL, NULL, 0, 16, 0, "content-encoding", NULL },
{ NULL, NULL, 0, 16, 0, "content-language", NULL },
{ NULL, NULL, 0, 14, 0, "content-length", NULL },
{ NULL, NULL, 0, 16, 0, "content-location", NULL },
{ NULL, NULL, 0, 13, 0, "content-range", NULL },
{ NULL, NULL, 0, 12, 0, "content-type", NULL },
{ NULL, NULL, 0, 6, 0, "cookie", NULL },
{ NULL, NULL, 0, 4, 0, "date", NULL },
{ NULL, NULL, 0, 4, 0, "etag", NULL },
{ NULL, NULL, 0, 6, 0, "expect", NULL },
{ NULL, NULL, 0, 7, 0, "expires", NULL },
{ NULL, NULL, 0, 4, 0, "from", NULL },
{ NULL, NULL, 0, 4, 0, "host", NULL },
{ NULL, NULL, 0, 8, 0, "if-match", NULL },
{ NULL, NULL, 0, 17, 0, "if-modified-since", NULL },
{ NULL, NULL, 0, 13, 0, "if-none-match", NULL },
{ NULL, NULL, 0, 8, 0, "if-range", NULL },
{ NULL, NULL, 0, 19, 0, "if-unmodified-since", NULL },
{ NULL, NULL, 0, 13, 0, "last-modified", NULL },
{ NULL, NULL, 0, 4, 0, "link", NULL },
{ NULL, NULL, 0, 8, 0, "location", NULL },
{ NULL, NULL, 0, 12, 0, "max-forwards", NULL },
{ NULL, NULL, 0, 18, 0, "proxy-authenticate", NULL },
{ NULL, NULL, 0, 19, 0, "proxy-authorization", NULL },
{ NULL, NULL, 0, 5, 0, "range", NULL },
{ NULL, NULL, 0, 7, 0, "referer", NULL },
{ NULL, NULL, 0, 7, 0, "refresh", NULL },
{ NULL, NULL, 0, 11, 0, "retry-after", NULL },
{ NULL, NULL, 0, 6, 0, "server", NULL },
{ NULL, NULL, 0, 10, 0, "set-cookie", NULL },
{ NULL, NULL, 0, 25, 0, "strict-transport-security", NULL },
{ NULL, NULL, 0, 17, 0, "transfer-encoding", NULL },
{ NULL, NULL, 0, 10, 0, "user-agent", NULL },
{ NULL, NULL, 0, 4, 0, "vary", NULL },
{ NULL, NULL, 0, 3, 0, "via", NULL },
{ NULL, NULL, 0, 16, 0, "www-authenticate", NULL }
};
/********************/
/* external storage */
/********************/
extern BOOL Http2Enabled;
extern uint Http2MaxHeaderTableSize;
extern int ToLowerCase[];
extern char ErrorSanityCheck[];
extern CONFIG_STRUCT Config;
extern LIST_HEAD Http2List;
extern WATCH_STRUCT Watch;
/*****************************************************************************/
/*
Build a request from the header block. This involves decoding and
decompressing the various HTTP/2 header elements while reconstructing an
HTTP/1.1 request line and populating the request dictionary with equivalent
header fields. This is obviously closely integrated with the request structure
and necessarily parallels processing in RequestGet() and RequestFields().
The continuation frame handling is elementary but (probably) sufficient to
purpose.
If |flags| is -1 then for development purposes provide a module WATCHing report
for a *response* header.
*/
int HpackHeadersFrame
(
HPACK_TABLE_STRUCT *tabptr,
uint flags,
uint ident,
uchar *BlockPtr,
uint BlockLength
)
{
#define BUFFER_INCREMENT 4096
#define COOKIE_INCREMENT 1024
static uint CookieSize;
static uchar *CookiePtr;
int nlen, retval, vlen,
AuthorityLength,
CompressedLength,
CookieLength,
/* equivalent of HTTP/1.1 request header */
HeaderLength,
MethodLength,
PathLength;
uint blen, bytes, depend, endhead, endstream,
index, length, padlen, weight;
uchar byte;
uchar *bptr, *bzptr, *cptr, *nptr, *sptr, *vptr, *zptr;
uchar *AuthorityPtr,
*MethodPtr,
*PathPtr;
DICT_ENTRY_STRUCT *denptr;
HTTP2_STRUCT *h2ptr;
REQUEST_STRUCT *rqptr;
HTTP2_STREAM_STRUCT *s2ptr;
#if WATCH_MOD
uchar ResponseHeader [2048];
#endif
/*********/
/* begin */
/*********/
h2ptr = tabptr->h2ptr;
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackHeadersFrame() 0x!2XL !&X !UL",
flags, BlockPtr, BlockLength);
#if WATCH_MOD
if (flags == (uint)-1)
zptr = (sptr = ResponseHeader) + sizeof(ResponseHeader)-1;
else
#endif /* WATCH_MOD */
{
/* locate the request corresponding to the stream (ident) */
for (s2ptr = LIST_GET_HEAD(&h2ptr->StreamList);
s2ptr != NULL;
s2ptr = LIST_GET_NEXT(s2ptr))
if (s2ptr->Ident == ident) break;
if (s2ptr == NULL)
{
/* no stream (request) found with that ident so create one */
if ((rqptr = Http2RequestBegin (h2ptr, ident)) == NULL)
return (-HTTP2_ERROR_REFUSED);
s2ptr = &rqptr->Http2Stream;
}
else
rqptr = (REQUEST_STRUCT*)s2ptr->RequestPtr;
}
bzptr = (bptr = BlockPtr) + BlockLength;
#if WATCH_MOD
if (flags == (uint)-1)
endhead = endstream = depend = padlen = weight = 0;
else
#endif /* WATCH_MOD */
{
/*********/
/* flags */
/*********/
if (flags & HTTP2_FLAG_HEAD_PADDED)
HTTP2_GET_8 (bptr, padlen)
else
padlen = 0;
if (flags & HTTP2_FLAG_HEAD_PRIORITY)
{
HTTP2_GET_32 (bptr, depend)
HTTP2_GET_8 (bptr, weight)
}
else
depend = weight = 0;
endhead = (flags & HTTP2_FLAG_HEAD_END_HEAD);
endstream = (flags & HTTP2_FLAG_HEAD_END_STR);
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"endhead:!&B endstream:!&B depend:!UL pad:!UL weight:!UL",
endhead, endstream, depend, padlen, weight);
if ((bptr -= padlen) >= bzptr) return (-HTTP2_ERROR_PROTOCOL);
/* reset the original parameters against any padding, etc. */
BlockLength -= bptr - BlockPtr;
BlockPtr = bptr;
/* handle continuation frames */
if (!endhead || s2ptr->ContinPtr)
{
/****************/
/* continuation */
/****************/
/* impose a (somewhat ambit) limit on (compressed) header list size */
if (s2ptr->ContinSize >= h2ptr->ServerMaxHeaderListSize)
{
if (WATCHING (rqptr, WATCH_HTTP2))
WatchThis (WATCHITM(rqptr), WATCH_HTTP2,
"HTTP/2 continuation !UL exceeds !UL bytes",
s2ptr->ContinSize, h2ptr->ServerMaxHeaderListSize);
return (-HTTP2_ERROR_PROTOCOL);
}
/* buffer this frame content for subsequent processing */
bytes = s2ptr->ContinSize + BlockLength;
s2ptr->ContinPtr = VmReallocHeap (rqptr, s2ptr->ContinPtr,
bytes, FI_LI);
memcpy (s2ptr->ContinPtr + s2ptr->ContinSize, BlockPtr, BlockLength);
s2ptr->ContinSize = bytes;
/* if this is not the last of the headers data */
if (!endhead) return (0);
/* use the buffered headers data */
BlockLength = rqptr->Http2Stream.ContinSize;
BlockPtr = rqptr->Http2Stream.ContinPtr;
}
}
AuthorityPtr = MethodPtr = PathPtr = NULL;
AuthorityLength = CookieLength = HeaderLength =
MethodLength = PathLength = 0;
CompressedLength = BlockLength;
h2ptr->HpackClientInputCount += BlockLength;
bzptr = (bptr = BlockPtr) + BlockLength;
while (bptr < bzptr)
{
nptr = vptr = NULL;
nlen = vlen = 0;
byte = *bptr;
if (byte & 0x80)
{
/***********************/
/* indexed field (6.1) */
/***********************/
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "6.1");
retval = HpackDecodeInt32 (h2ptr, &bptr, bzptr, 7, &index);
if (retval < 0) return (retval);
if (!index) return (-HTTP2_ERROR_PROTOCOL);
retval = HpackGetIndex (tabptr, index, &nptr, &nlen, &vptr, &vlen);
if (retval < 0) return (retval);
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"index:!UL \"!AZ\" \"!AZ\"", index, nptr, vptr);
}
else
if (((byte & 0xc0) == 0x40) ||
((byte & 0xf0) == 0x00) ||
((byte & 0xf0) == 0x10))
{
/************************/
/* literal header (6.2) */
/************************/
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
{
if ((byte & 0xc0) == 0x40) cptr = "6.2.1"; else
if ((byte & 0xf0) == 0x00) cptr = "6.2.2"; else cptr = "6.2.3";
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "!AZ", cptr);
}
if ((byte & 0xc0) == 0x40)
retval = HpackDecodeInt32 (h2ptr, &bptr, bzptr, 6, &index);
else
retval = HpackDecodeInt32 (h2ptr, &bptr, bzptr, 4, &index);
if (retval < 0) return (retval);
if (index)
{
/* non-zero index, value only */
retval = HpackGetIndex (tabptr, index, &nptr, &nlen, &vptr, &vlen);
if (retval < 0) return (retval);
retval = HpackDecodeString (h2ptr, &bptr, bzptr, &vptr, &vlen);
if (retval < 0) return (retval);
}
else
{
/* zero index, name then value */
retval = HpackDecodeString (h2ptr, &bptr, bzptr, &nptr, &nlen);
if (retval < 0) return (retval);
retval = HpackDecodeString (h2ptr, &bptr, bzptr, &vptr, &vlen);
if (retval < 0) return (retval);
}
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"index:!UL \"!AZ\" \"!AZ\"", index, nptr, vptr);
if ((byte & 0xc0) == 0x40)
{
/* similar field name constraints as RequestFields() */
if (nlen > MAX_REQUEST_FIELD_NAME_LEN)
{
if (WATCHING (rqptr, WATCH_HTTP2))
WatchThis (WATCHITM(rqptr), WATCH_HTTP2,
"FIELD \"!32AZ..\" exceeds !UL bytes",
nptr, MAX_REQUEST_FIELD_NAME_LEN);
#if WATCH_MOD
FaoToStdout ("!AZ:!UL FIELD \"!32AZ..\" exceeds !UL bytes",
FI_LI, nptr, MAX_REQUEST_FIELD_NAME_LEN);
#endif
return (-HTTP2_ERROR_REFUSED);
}
zptr = (cptr = nptr) + nlen;
/* e.g. ":authority" */
if (*cptr == ':') cptr++;
while (cptr < zptr && NOTCTL(*cptr) && NOTSEP(*cptr)) cptr++;
if (cptr < zptr)
{
if (WATCHING (rqptr, WATCH_HTTP2))
WatchThis (WATCHITM(rqptr), WATCH_HTTP2,
"FIELD \"!#AZ\" char \\x!2XL at !UL",
nlen, nptr, *cptr, cptr - nptr);
#if WATCH_MOD
FaoToStdout ("%HTTPD-W-NOTICED, !20%D, !AZ:!UL, "
"FIELD \"!#AZ\" char \\x!2XL at !UL",
0, FI_LI, nlen, nptr, *cptr, cptr - nptr);
#endif
return (-HTTP2_ERROR_REFUSED);
}
retval = HpackAddToTable (tabptr, nptr, nlen, vptr, vlen);
if (retval < 0) return (retval);
}
}
else
if ((byte & 0xe0) == 0x20)
{
/******************************/
/* dynamic table update (6.3) */
/******************************/
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "6.3");
retval = HpackDecodeInt32 (h2ptr, &bptr, bzptr, 5, &length);
if (retval < 0) return (retval);
if (h2ptr->ClientMaxHeaderTableSize)
{
if (length <= h2ptr->ClientMaxHeaderTableSize)
{
h2ptr->HpackClientTable.max = length;
HpackTrimTable (tabptr);
}
else
return (-HTTP2_ERROR_PROTOCOL);
}
else
{
/* if client hasn't set a maximum then (almost) free-for-all */
if (length <= HTTP2_MAX_HEAD_TAB_SIZE)
{
h2ptr->HpackClientTable.max = length;
HpackTrimTable (tabptr);
}
else
return (-HTTP2_ERROR_INTERNAL);
}
}
else
{
/********/
/* hmmm */
/********/
ErrorNoticed (NULL, SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
return (-HTTP2_ERROR_PROTOCOL);
}
/*****************/
/* build request */
/*****************/
if (nptr && vptr)
{
#if WATCH_MOD
if (flags == (uint)-1 && MATCH7 (nptr, ":status"))
{
/* only when WATCHing the response header */
for (cptr = "HTTP/1.1 "; *cptr && sptr < zptr; *sptr++ = *cptr++);
for (cptr = vptr; *cptr && sptr < zptr; *sptr++ = *cptr++);
if (sptr < zptr) *sptr++ = '\r';
if (sptr < zptr) *sptr++ = '\n';
*sptr = '\0';
}
else
#endif /* WATCH_MOD */
if (MATCH10 (nptr, ":authority"))
{
/* RFC7540 8.1.2.3 ... analogue to 'Host:" */
denptr = DictInsert (rqptr->rqDictPtr, DICT_TYPE_INTERNAL,
"request_authority", 17, vptr, vlen);
AuthorityPtr = DICT_GET_VALUE(denptr);
AuthorityLength = DICT_GET_VALUE_LEN(denptr);
}
else
if (MATCH7 (nptr, ":method"))
{
denptr = DictInsert (rqptr->rqDictPtr, DICT_TYPE_INTERNAL,
"request_method", 14, vptr, vlen);
MethodPtr = DICT_GET_VALUE(denptr);
MethodLength = DICT_GET_VALUE_LEN(denptr);
}
else
if (MATCH5 (nptr, ":path"))
{
denptr = DictInsert (rqptr->rqDictPtr, DICT_TYPE_INTERNAL,
"request_path", 12, vptr, vlen);
PathPtr = DICT_GET_VALUE(denptr);
PathLength = DICT_GET_VALUE_LEN(denptr);
}
else
#if WATCH_MOD
/* only when WATCHing the response header */
if (flags != (uint)-1 && MATCH7 (nptr, "cookie"))
#else
if (MATCH7 (nptr, "cookie"))
#endif
{
/* RFC7540 8.1.2.5 multiple HTTP/2 into single HTTP/1.n cookie */
uchar *cptr, *sptr, *zptr;
length = vlen;
if (CookieLength) length += 2;
if (CookieLength + length > CookieSize)
{
while (CookieLength + length > CookieSize)
CookieSize += COOKIE_INCREMENT;
CookiePtr = VmRealloc (CookiePtr, CookieSize, FI_LI);
}
zptr = (sptr = CookiePtr) + CookieSize;
sptr += CookieLength;
if (CookieLength)
{
/* length += 2; */
*sptr++ = ';';
*sptr++ = ' ';
}
for (cptr = vptr; *cptr && sptr < zptr; *sptr++ = *cptr++);
*sptr = '\0';
CookieLength = sptr - CookiePtr;
}
else
if (*nptr != ':')
{
#if WATCH_MOD
if (flags == (uint)-1)
{
/* only when WATCHing the response header */
for (cptr = nptr; *cptr && sptr < zptr; *sptr++ = *cptr++);
for (cptr = ": "; *cptr && sptr < zptr; *sptr++ = *cptr++);
for (cptr = vptr; *cptr && sptr < zptr; *sptr++ = *cptr++);
if (sptr < zptr) *sptr++ = '\r';
if (sptr < zptr) *sptr++ = '\n';
*sptr = '\0';
}
else
#endif /* WATCH_MOD */
{
DictInsert (rqptr->rqDictPtr, DICT_TYPE_REQUEST,
nptr, nlen, vptr, vlen);
/* RFC7540 section 6.5.2 */
HeaderLength += nlen + vlen + 32;
h2ptr->HpackClientOutputCount += nlen + vlen + 4;
}
}
/* impose the limit on (uncompressed) header list size */
if (HeaderLength + CookieLength+32 >= h2ptr->ServerMaxHeaderListSize)
{
/* RFC7540 section 6.5.2 SETTINGS_MAX_HEADER_LIST_SIZE */
if (WATCHING (rqptr, WATCH_HTTP2))
WatchThis (WATCHITM(rqptr), WATCH_HTTP2,
"HTTP/2 header !UL exceeds !UL bytes",
HeaderLength + CookieLength+32,
h2ptr->ServerMaxHeaderListSize);
return (-HTTP2_ERROR_PROTOCOL);
}
}
}
if (CookieLength)
{
DictInsert (rqptr->rqDictPtr, DICT_TYPE_REQUEST,
"cookie", 6, CookiePtr, CookieLength);
HeaderLength += CookieLength + 2;
h2ptr->HpackClientOutputCount += CookieLength + 2;
}
#if WATCH_MOD
if (flags != (uint)-1)
#endif
{
/* create a request line */
if (MethodPtr && PathPtr)
{
/* reserve space in the dictionary entry then populate */
bytes = MethodLength + PathLength + 10;
denptr = DictInsert (rqptr->rqDictPtr, DICT_TYPE_INTERNAL,
"request_line", 12, NULL, bytes);
zptr = (sptr = bptr = DICT_GET_VALUE (denptr)) + bytes;
rqptr->rqHeader.RequestLinePtr = bptr;
for (cptr = MethodPtr; *cptr && sptr < zptr; *sptr++ = *cptr++);
if (sptr < zptr) *sptr++ = ' ';
for (cptr = PathPtr; *cptr && sptr < zptr; *sptr++ = *cptr++);
for (cptr = " HTTP/1.1"; *cptr && sptr < zptr; *sptr++ = *cptr++);
DictValueLength (denptr, sptr - bptr);
rqptr->rqHeader.RequestLineLength = DICT_GET_VALUE_LEN(denptr);
h2ptr->HpackClientOutputCount += bytes + 2;
HeaderLength += bytes + 2;
/* similar request line constraints as RequestGet() */
for (cptr = bptr; *cptr && NOTCTL(*cptr); cptr++);
if (*cptr)
{
if (WATCHING (rqptr, WATCH_HTTP2))
WatchThis (WATCHITM(rqptr), WATCH_HTTP2,
"CHAR \\x!2XL at !UL", *cptr, cptr - bptr);
#if WATCH_MOD
FaoToStdout ("%HTTPD-W-NOTICED, !20%D, !AZ:!UL, "
"CHAR \\x!2XL at !UL",
0, FI_LI, *cptr, cptr - bptr);
#endif
return (-HTTP2_ERROR_REFUSED);
}
/* HTTP/1.1 mandates a host field */
if (!DictLookup (rqptr->rqDictPtr, DICT_TYPE_REQUEST, "host", 4))
{
/* the request has not supplied an explicit "host" field */
if (AuthorityPtr)
{
/* use the ":authority" pseudo-header RFC7540 8.1.2.3 */
DictInsert (rqptr->rqDictPtr, DICT_TYPE_REQUEST, "host", 4,
AuthorityPtr, AuthorityLength);
HeaderLength += AuthorityLength + 6;
h2ptr->HpackClientOutputCount += AuthorityLength + 6;
}
else
{
/* fall back to the current virtual host */
DictInsert (rqptr->rqDictPtr, DICT_TYPE_REQUEST, "host", 4,
h2ptr->ServicePtr->ServerHostPort,
h2ptr->ServicePtr->ServerHostPortLength);
HeaderLength += h2ptr->ServicePtr->ServerHostPortLength + 6;
h2ptr->HpackClientOutputCount +=
h2ptr->ServicePtr->ServerHostPortLength + 6;
}
}
}
else
return (-HTTP2_ERROR_INTERNAL);
}
/**********/
/* voila! */
/**********/
#if WATCH_MOD
if (flags != (uint)-1)
#endif
{
/* as if terminating blank line */
HeaderLength += 2;
if (WATCHING (h2ptr, WATCH_HTTP2) &&
!WATCHING1S(h2ptr) && !WATCHING2(h2ptr))
WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
"REQUEST header !UL->!UL !UL%",
CompressedLength, HeaderLength,
(CompressedLength * 100) / HeaderLength);
/* keep the accounting representative */
rqptr->NetIoPtr->BlocksRawRx64++;
rqptr->NetIoPtr->BlocksTallyRx64++;
rqptr->NetIoPtr->BytesRawRx64 += CompressedLength;
rqptr->NetIoPtr->BytesTallyRx64 += CompressedLength;
rqptr->BytesRx64 += CompressedLength;
if (s2ptr->ContinPtr)
{
VmFreeFromHeap (rqptr, s2ptr->ContinPtr, FI_LI);
s2ptr->ContinSize = 0;
s2ptr->ContinPtr = NULL;
}
Http2RequestProcess (rqptr);
}
return (0);
}
/*****************************************************************************/
/*
Decode a 32 bit, unsigned integer. Return zero for success, or a negated error
code. h2o decode_int() function reimagined.
*/
int HpackDecodeInt32
(
HTTP2_STRUCT *h2ptr,
uchar **baddr,
uchar *bzptr,
uint prebits,
uint *iptr
)
{
uint mult, premask, preval, value;
uchar premax;
uchar *bptr;
/*********/
/* begin */
/*********/
bptr = *baddr;
if (bptr >= bzptr) return (-HTTP2_ERROR_PROTOCOL);
premax = (1 << prebits) - 1;
value = (uint)*bptr++ & premax;
if (value != premax)
{
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackDecodeInt32() !UL 1", value);
*iptr = value;
*baddr = bptr;
return (0);
}
premask = 0x80000000;
value = preval = premax;
for (mult = 1;; mult *= 128)
{
if (bptr >= bzptr) return (0);
value += (*bptr & 0x7f) * mult;
if (!(*bptr++ & 0x80)) break;
/* detect integer overflow (most significant bit reset) */
if ((preval & premask) && !(value & premask)) break;
preval = value;
}
if ((preval & premask) && !(value & premask))
{
if (WATCHING (h2ptr, WATCH_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_HTTP2, "OVERFLOW integer");
*iptr = 0;
*baddr = bptr;
return (-HTTP2_ERROR_PROTOCOL);
}
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackDecodeInt32() !UL", value);
*iptr = value;
*baddr = bptr;
return (0);
}
/*****************************************************************************/
/*
Encode a 32 bit, unsigned integer. h2o encode_int() function reimagined.
*/
int HpackEncodeInt32
(
HTTP2_STRUCT *h2ptr,
uchar **baddr,
uchar *bzptr,
uint prebits,
uint value
)
{
uchar *bptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackEncodeInt32() !UL !UL", value, prebits);
bptr = *baddr;
if (bptr >= bzptr) return (-HTTP2_ERROR_INTERNAL);
if (value < (1 << prebits) - 1)
*bptr++ |= value;
else
{
value -= (1 << prebits) - 1;
*bptr++ |= (1 << prebits) - 1;
if (bptr >= bzptr) return (-HTTP2_ERROR_INTERNAL);
for (; value >= 128; value >>= 7) *bptr++ = 0x80 | value;
*bptr++ = value;
if (bptr >= bzptr) return (-HTTP2_ERROR_INTERNAL);
}
*baddr = bptr;
return (0);
}
/*****************************************************************************/
/*
Decode a header string pointed at by the address at |baddr|. Return the
number of characters in that string (0..n), or a negated error code. The
string is located in an internal buffer at the address contained by |raddr|
and must be used in situ or copied to an alternate buffer.
*/
int HpackDecodeString
(
HTTP2_STRUCT *h2ptr,
uchar **baddr,
uchar *bzptr,
uchar **raddr,
uint **rlenaddr
)
{
static uint idx, idxcnt;
static uint buflen [2];
static uchar *bufptr [2];
BOOL ishenc;
int eos = 1;
uint length, retval;
uchar state = 0;
uchar *bptr, *sptr, *zptr;
nghttp2_huff_decode *entry;
/*********/
/* begin */
/*********/
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackDecodeString() ishenc:!&B", **baddr & 0x80);
bptr = *baddr;
/* is it huffman encoded? */
ishenc = *bptr & 0x80;
retval = HpackDecodeInt32 (h2ptr, &bptr, bzptr, 7, &length);
if (retval < 0) return (retval);
/* ensure there's enough data available */
if (bptr + length > bzptr)
{
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "ERROR_PROTOCOL");
return (-HTTP2_ERROR_PROTOCOL);
}
/* maintain two buffers (concurrent name and value) */
idx = idxcnt++ & 1;
/* max huffman compression ratio is <= 0.5 */
if (length * 2 > buflen[idx])
{
for (buflen[idx] = 256; buflen[idx] < length * 2; buflen[idx] *= 2);
if (bufptr[idx]) VmFree (bufptr[idx], FI_LI);
bufptr[idx] = VmGet (buflen[idx]);
}
sptr = bufptr[idx];
if (ishenc)
{
/* h2o decode_huffman() function reimagined */
for (zptr = bptr + length; bptr < zptr; bptr++)
{
/* h2o huffdecode4() function reimagined (and inlined) */
entry = huff_decode_table[state] + (*bptr >> 4);
if ((entry->flags & NGHTTP2_HUFF_FAIL) != 0)
{
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "ERROR_PROTOCOL");
return (-HTTP2_ERROR_PROTOCOL);
}
if ((entry->flags & NGHTTP2_HUFF_SYM) != 0) *sptr++ = entry->sym;
state = entry->state;
eos = (entry->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
entry = huff_decode_table[state] + (*bptr & 0xf);
if ((entry->flags & NGHTTP2_HUFF_FAIL) != 0)
{
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "ERROR_PROTOCOL");
return (-HTTP2_ERROR_PROTOCOL);
}
if ((entry->flags & NGHTTP2_HUFF_SYM) != 0) *sptr++ = entry->sym;
state = entry->state;
eos = (entry->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
}
if (!eos)
{
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "ERROR_PROTOCOL");
return (-HTTP2_ERROR_PROTOCOL);
}
}
else
for (zptr = bptr + length; bptr < zptr; *sptr++ = *bptr++);
*sptr = '\0';
*baddr = bptr;
*raddr = bufptr[idx];
*rlenaddr = sptr - bufptr[idx];
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "!UL->!UL !UL% |!AZ",
length, sptr-bufptr[idx],
(sptr-bufptr[idx]) ? length * 100 / (sptr-bufptr[idx]) : 0,
bufptr[idx]);
return (0);
}
/*****************************************************************************/
/*
Encode a string.
*/
int HpackEncodeString
(
HTTP2_STRUCT *h2ptr,
uchar **baddr,
uchar *bzptr,
uchar *sptr,
uint slen
)
{
#define ENDODE_HUFFMAN 1
int retval;
uchar *bptr;
#if ENCODE_HUFFMAN
int clen;
uint bits, remain;
uchar *cptr;
nghttp2_huff_sym *hsptr;
#endif
/*********/
/* begin */
/*********/
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackEncodeString() !UL !AZ", slen, sptr);
bptr = *baddr;
if (bptr >= bzptr) return (-HTTP2_ERROR_INTERNAL);
*bptr = 0;
#if ENCODE_HUFFMAN
/* h2o encode_huffman() function reimagined (and inlined) */
if (slen)
{
/* first calculate the length of the encoded string */
bits = 0;
remain = 40;
clen = slen;
cptr = sptr;
while (clen)
{
hsptr = huff_decode_table + *cptr++;
bits |= (uint)hsptr->code << (remain - hsptr->nbits);
remain -= hsptr->nbits;
while (remain <= 32)
{
bptr++;
bits <<= 8;
remain += 8;
if (--clen == 0) break;
}
}
if (remain != 40)
{
bits |= (1 << remain) - 1;
bptr++;
}
clen = bptr - *baddr;
/* then insert that length as an integer */
bptr = *baddr;
retval = HpackEncodeInt32 (h2ptr, baddr, bzptr, 7, clen);
if (retval < 0) return (retval);
/* and OR-in the huffman-encoded flag */
*bptr |= 0x80;
bptr = *baddr;
if (bptr >= bzptr) return (-HTTP2_ERROR_INTERNAL);
/* now actually huffman encode the string */
bits = 0;
remain = 40;
clen = slen;
cptr = sptr;
while (clen)
{
hsptr = huff_decode_table + *cptr++;
bits |= (uint)hsptr->code << (remain - hsptr->nbits);
remain -= hsptr->nbits;
while (remain <= 32)
{
*bptr++ = bits >> 32;
bits <<= 8;
remain += 8;
if (--clen == 0) break;
}
if (bptr >= bzptr) return (-HTTP2_ERROR_INTERNAL);
}
if (remain != 40)
{
bits |= (1 << remain) - 1;
*bptr++ = bits >> 32;
if (bptr >= bzptr) return (-HTTP2_ERROR_INTERNAL);
}
}
else
{
retval = HpackEncodeInt32 (h2ptr, &bptr, bzptr, 7, slen);
if (retval < 0) return (retval);
}
#else /* ENCODE_HUFFMAN */
retval = HpackEncodeInt32 (h2ptr, &bptr, bzptr, 7, slen);
if (retval < 0) return (retval);
while (slen-- && bptr < bzptr) *bptr++ = *sptr++;
#endif /* ENCODE_HUFFMAN */
if (bptr >= bzptr) return (-HTTP2_ERROR_INTERNAL);
*baddr = bptr;
return (0);
}
/*****************************************************************************/
/*
Return the length of the value (>=0) if hit, negative error code if lookup
failed. Name and value pointers are updated if hit, not modified if lookup
failed. Used by both client (request headers) and server (response headers)
dynamic tables.
*/
int HpackGetIndex
(
HPACK_TABLE_STRUCT *tabptr,
uint index,
uchar **naddr,
uint **nlenaddr,
uchar **vaddr,
uint **vlenaddr
)
{
int count;
uchar *nptr, *vptr;
HPACK_ENTRY_STRUCT *heptr;
HTTP2_STRUCT *h2ptr;
/*********/
/* begin */
/*********/
h2ptr = tabptr->h2ptr;
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackGetIndex() table:!AZ index:!UL",
tabptr == &h2ptr->HpackClientTable ? "CLIENT" : "SERVER",
index);
if (!index) return (-HTTP2_ERROR_PROTOCOL);
if (index <= HPACK_STATIC_TABLE_COUNT)
heptr = &HpackStaticTable[index];
else
{
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
HpackWatchTable (tabptr);
if (heptr = tabptr->head)
for (count = HPACK_STATIC_TABLE_COUNT + 1;
heptr && count < index;
count++)
heptr = heptr->blink;
}
if (heptr)
{
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "!&Z=!&Z",
heptr->name, heptr->value);
*naddr = heptr->name;
*nlenaddr = heptr->nlen;
*vaddr = heptr->value;
*vlenaddr = heptr->vlen;
heptr->hits++;
return (0);
}
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "HTTP2_ERROR_INTERNAL");
return (-HTTP2_ERROR_INTERNAL);
}
/*****************************************************************************/
/*
Insert the supplied name/value pair at the head of the dynamic table as
described in RFC7541 section 4. Used to maintain both client (request headers)
and server (response headers) dynamic tables.
*/
int HpackAddToTable
(
HPACK_TABLE_STRUCT *tabptr,
uchar *nptr,
uint nlen,
uchar *vptr,
uint vlen
)
{
int size;
uchar *sptr;
HPACK_ENTRY_STRUCT *blink, *heptr;
HTTP2_STRUCT *h2ptr;
/*********/
/* begin */
/*********/
h2ptr = tabptr->h2ptr;
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
{
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackAddToTable() table:!AZ !SL|!AZ| !SL|!AZ",
tabptr == &h2ptr->HpackClientTable ? "CLIENT" : "SERVER",
nlen, nptr, vlen, vptr);
HpackWatchTable (tabptr);
}
if (!nlen) return (-HTTP2_ERROR_INTERNAL);
/* RFC7541 4.1 specifies 32 bytes plus the two strings */
size = nlen + vlen + sizeof(HPACK_ENTRY_STRUCT);
if (size > tabptr->max)
{
if (WATCHING (h2ptr, WATCH_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
"entry !UL > !UL bytes", size, tabptr->max);
return (-HTTP2_ERROR_INTERNAL);
}
tabptr->size += size;
heptr = VmGet2Heap (h2ptr, size);
heptr->nlen = nlen;
heptr->vlen = vlen;
for (heptr->name = sptr = heptr->buffer; nlen--; *sptr++ = *nptr++);
/* null is accomodated by the |buffer[4]| space */
*sptr++ = '\0';
for (heptr->value = sptr; vlen--; *sptr++ = *vptr++);
/* null is accomodated by the |buffer[4]| space */
*sptr = '\0';
/* insert at head of table */
blink = tabptr->head;
tabptr->head = heptr;
if (blink == NULL)
tabptr->tail = heptr;
else
{
heptr->flink = NULL;
heptr->blink = blink;
blink->flink = heptr;
}
tabptr->count++;
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
HpackWatchTable (tabptr);
if (tabptr->size > tabptr->max) HpackTrimTable (tabptr);
return (0);
}
/*****************************************************************************/
/*
Find the name and value combination, or simply name, in the static/dynamic
table. Used to maintain server (response headers) dynamic table. Baseline the
search of the table at the number of entries before beginning processing the
current set of response headers. That is, do not use any entries added during
processing of the current headers! Return the index number or zero to indicate
not found.
*/
int HpackFindInTable
(
HPACK_TABLE_STRUCT *tabptr,
uchar *nptr,
uint nlen,
uchar *vptr,
uint vlen
)
{
int index;
HPACK_ENTRY_STRUCT *heptr;
HTTP2_STRUCT *h2ptr;
/*********/
/* begin */
/*********/
h2ptr = tabptr->h2ptr;
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackFindInTable() table:!AZ !UL|!AZ| !UL|!AZ",
tabptr == &h2ptr->HpackClientTable ? "CLIENT" : "SERVER",
nlen, nptr, vlen, vptr);
if (nptr == NULL)
{
/* set a baseline before the start of response header processing */
tabptr->baseline = HPACK_STATIC_TABLE_COUNT + tabptr->count;
return (0);
}
/* search the static table for a corresponding entry */
for (index = 1; index <= HPACK_STATIC_TABLE_COUNT; index++)
{
heptr = &HpackStaticTable[index];
if (heptr->nlen != nlen) continue;
if (heptr->vlen != vlen) continue;
if (!MATCH4 (heptr->name, nptr)) continue;
if (vptr && heptr->value && !MATCH4 (heptr->value, vptr)) continue;
if (strcmp (heptr->name, nptr)) continue;
if (vptr && heptr->value && strcmp (heptr->value, vptr)) continue;
/* hit it! */
break;
}
if (index > HPACK_STATIC_TABLE_COUNT)
{
/* search the dynamic table for a corresponding entry */
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
HpackWatchTable (tabptr);
for (heptr = tabptr->head; heptr != NULL; index++, heptr = heptr->blink)
{
if (index > tabptr->baseline)
{
/* limit search to entries made prior to this response */
heptr = NULL;
break;
}
if (heptr->nlen != nlen) continue;
if (heptr->vlen != vlen) continue;
if (!MATCH4 (heptr->name, nptr)) continue;
if (vptr && !MATCH4 (heptr->value, vptr)) continue;
if (strcmp (heptr->name, nptr)) continue;
if (vptr && strcmp (heptr->value, vptr)) continue;
/* finally! */
heptr->hits++;
break;
}
}
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"index:!UL", heptr ? index : 0);
if (heptr) return (index);
return (0);
}
/*****************************************************************************/
/*
Beginning at the tail of the dynamic table, trim entries until the size is less
than the table maximum size according to RFC7541 section 4. Used to maintain
both client (request headers) and server (response headers) dynamic tables.
*/
void HpackTrimTable (HPACK_TABLE_STRUCT *tabptr)
{
int size;
HPACK_ENTRY_STRUCT *flink, *heptr;
HTTP2_STRUCT *h2ptr;
/*********/
/* begin */
/*********/
h2ptr = tabptr->h2ptr;
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
{
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HpackTrimTable() table:!AZ size:!UL max!UL",
tabptr == &h2ptr->HpackClientTable ? "CLIENT" : "SERVER",
tabptr->size, tabptr->max);
HpackWatchTable (tabptr);
}
if (tabptr->size <= tabptr->max) return;
heptr = tabptr->tail;
while (heptr != NULL)
{
if (tabptr->size <= tabptr->max) break;
size = heptr->nlen + heptr->vlen + sizeof(HPACK_ENTRY_STRUCT);
flink = heptr->flink;
VmFreeFrom2Heap (h2ptr, heptr, FI_LI);
/* if top of list */
if ((heptr = flink) == NULL) break;
heptr->blink = NULL;
tabptr->tail = heptr;
tabptr->size -= size;
tabptr->count--;
if (HPACK_STATIC_TABLE_COUNT + tabptr->count < tabptr->baseline)
tabptr->baseline = HPACK_STATIC_TABLE_COUNT + tabptr->count;
}
/* if an empty table (maximum size of zero) */
if (tabptr->tail == NULL) tabptr->head = NULL;
if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
HpackWatchTable (tabptr);
}
/*****************************************************************************/
/*
*/
#ifdef WATCH_MOD
void HpackWatchTable (HPACK_TABLE_STRUCT *tabptr)
{
int count;
HPACK_ENTRY_STRUCT *heptr;
HTTP2_STRUCT *h2ptr;
/*********/
/* begin */
/*********/
h2ptr = tabptr->h2ptr;
WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2,
"HPACK table:!AZ count:!UL baseline:!UL head:!&X tail:!&X size:!UL max:!UL",
tabptr == &h2ptr->HpackClientTable ? "CLIENT" : "SERVER",
tabptr->count, tabptr->baseline, tabptr->head, tabptr->tail,
tabptr->size, tabptr->max);
/* need the [x]detail module WATCHing checkbox for the content as well! */
if (!(WATCH_MODULE(WATCH_MOD__OTHER) && WATCH_MODULE(WATCH_MOD__DETAIL)))
return;
count = HPACK_STATIC_TABLE_COUNT + 1;
for (heptr = tabptr->head; heptr; heptr = heptr->blink)
WatchDataFormatted ("!3ZL this:!&X flink:!&X blink:!&X \
!UL|!AZ| !UL|!AZ|\n",
count++, heptr,
heptr->flink, heptr->blink,
heptr->nlen, heptr->name,
heptr->vlen, heptr->value);
}
#endif /* WATCH_MOD */
/*****************************************************************************/
/*
*/
void HpackReport
(
REQUEST_STRUCT *rqptr,
uchar *ConnectNumber
)
{
static char TableBeginFao [] =
"<style type=\"text/css\">\n\
.datum { margin:0 0.4em 0 1em; font-weight:bold; \
text-decoration:underline; }\n\
.datum:first-of-type { margin:0 0.4em 0 0; }\n\
.hex { font-family:monospace; }\n\
.tbttl { font-size:110%; font-weight:bold; text-decoration:underline; \
padding-bottom:0.6em; }\n\
</style>\n\
<p><table class=\"ctgry\">\n\
<tr><td>\n\
<table class=\"lftlft\">\n"
#if WATCH_MOD
"<tr><td colspan=\"5\"></td>\
<th class=\"tbttl\">!AZ</th></tr>\n\
<tr><td></td>\
<td colspan=\"7\" style=\"padding-bottom:0.5em;\">"
#else
"<tr><td colspan=\"3\"></td>\
<th class=\"tbttl\">!AZ</th></tr>\n\
<tr><td></td>\
<td colspan=\"4\" style=\"padding-bottom:0.5em;\">"
#endif
"<span class=\"datum\">Count:</span>!UL\
<span class=\"datum\">Size:</span>!UL\
<span class=\"datum\">Maximum:</span>!UL\
<span class=\"datum\">Compression:</span>!UL%\
</td></tr>\n\
<tr><td></td>"
#if WATCH_MOD
"<th class=\"sbttl\">Entry</th>\
<th class=\"sbttl\">Flink</th>\
<th class=\"sbttl\">Blink</th>"
#endif
"<th class=\"sbttl targht\">Hits</th>\
<th class=\"sbttl\">Name</th>\
<th class=\"sbttl\">Value</th>\
</tr>\n";
static char TableEntryFao [] =
"<tr><th>!3ZL</th>"
#if WATCH_MOD
"<td class=\"hex\">!&X</td>\
<td class=\"hex\">!&X</td>\
<td class=\"hex\">!&X</td>"
#endif
"<td class=\"targht\">!UL</td>\
<td>!AZ</td>\
<td style=\"white-space:normal;\">!AZ</td>\
</tr>\n";
static char TableEndFao [] = "</table>\n</td></tr>\n</table>\n";
static char PageEndFao [] =
"!AZ\
</div>\n\
</body>\n\
</html>\n";
static char NotFoundFao [] =
"<p>HTTP/2 connection !UL not found!!\n\
</div>\n\
</body>\n\
</html>\n";
int count, percent, status;
ulong FaoVector [32];
ulong *vecptr;
uchar *cptr;
HPACK_ENTRY_STRUCT *heptr;
HPACK_TABLE_STRUCT *tabptr;
HTTP2_STRUCT *h2ptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_HTTP2))
WatchThis (WATCHITM(rqptr), WATCH_MOD_HTTP2, "HpackReport() !AZ !&X",
ConnectNumber, strtol (ConnectNumber, NULL, 16));
AdminPageTitle (rqptr, "HPACK Report");
/* find the specified HTTP2 entry */
count = atol (ConnectNumber);
for (h2ptr = LIST_GET_HEAD(&Http2List);
h2ptr != NULL;
h2ptr = LIST_GET_NEXT(h2ptr))
if (h2ptr->ConnectNumber == count) break;
if (h2ptr == NULL)
{
FaoToNet (rqptr, NotFoundFao, count);
AdminEnd (rqptr);
return;
}
for (tabptr = NULL;;)
{
if (tabptr == NULL)
{
tabptr = &h2ptr->HpackClientTable;
cptr = "Client";
if (h2ptr->HpackClientOutputCount)
percent = (h2ptr->HpackClientInputCount * 100) /
h2ptr->HpackClientOutputCount;
else
percent = 0;
}
else
if (tabptr == &h2ptr->HpackClientTable)
{
tabptr = &h2ptr->HpackServerTable;
cptr = "Server";
if (h2ptr->HpackServerInputCount)
percent = (h2ptr->HpackServerOutputCount * 100) /
h2ptr->HpackServerInputCount;
else
percent = 0;
}
else
break;
vecptr = FaoVector;
*vecptr++ = cptr;
*vecptr++ = tabptr->count;
*vecptr++ = tabptr->size;
*vecptr++ = tabptr->max;
*vecptr++ = percent;
status = FaolToNet (rqptr, TableBeginFao, &FaoVector);
if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
count = HPACK_STATIC_TABLE_COUNT + 1;
for (heptr = tabptr->head; heptr != NULL; heptr = heptr->blink)
{
vecptr = FaoVector;
*vecptr++ = count++;
#if WATCH_MOD
*vecptr++ = heptr;
*vecptr++ = heptr->flink;
*vecptr++ = heptr->blink;
#endif
*vecptr++ = heptr->hits;
*vecptr++ = heptr->name;
*vecptr++ = heptr->value;
status = FaolToNet (rqptr, TableEntryFao, &FaoVector);
if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
}
FaolToNet (rqptr, TableEndFao, NULL);
}
FaoToNet (rqptr, PageEndFao, AdminRefresh());
rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
ResponseHeader200 (rqptr, "text/html", NULL);
AdminEnd (rqptr);
}
/*****************************************************************************/