1 module hunt.http.codec.websocket.stream.ExtensionNegotiator;
2 
3 import hunt.http.codec.websocket.model.Extension;
4 import hunt.http.codec.websocket.model.ExtensionConfig;
5 import hunt.http.codec.websocket.model.extension.AbstractExtension;
6 import hunt.http.codec.websocket.model.extension.ExtensionFactory;
7 import hunt.http.codec.websocket.model.extension.WebSocketExtensionFactory;
8 
9 import hunt.http.HttpHeader;
10 import hunt.http.HttpMetaData;
11 import hunt.http.WebSocketConnection;
12 import hunt.http.WebSocketFrame;
13 
14 import hunt.collection;
15 import hunt.Exceptions;
16 
17 import std.algorithm;
18 import std.array;
19 import std.container.array;
20 import std.range;
21 
22 /**
23  * 
24  */
25 class ExtensionNegotiator {
26 
27     private ExtensionFactory factory;
28     private IncomingFrames nextIncomingFrames;
29     private OutgoingFrames nextOutgoingFrames;
30     private IncomingFrames incomingFrames;
31     private OutgoingFrames outgoingFrames;
32 
33     this() {
34         this(new WebSocketExtensionFactory());
35     }
36 
37     this(ExtensionFactory factory) {
38         this.factory = factory;
39     }
40 
41     ExtensionFactory getFactory() {
42         return factory;
43     }
44 
45     void setFactory(ExtensionFactory factory) {
46         this.factory = factory;
47     }
48 
49     ExtensionConfig[] negotiate(HttpMetaData metaData) {
50         InputRange!string fieldValues = metaData.getFields().getValues(HttpHeader.SEC_WEBSOCKET_EXTENSIONS.asString());
51 
52         Array!(ExtensionConfig) configList = ExtensionConfig.parseEnum(fieldValues);
53         auto r = configList[].filter!(c => factory.isAvailable(c.getName()));
54 
55         return r.array;
56         // return parseEnum(metaData.getFields().getValues(HttpHeader.SEC_WEBSOCKET_EXTENSIONS.asString()))
57         //         .stream().filter(c -> factory.isAvailable(c.getName()))
58         //         .collect(Collectors.toList());
59     }
60 
61     Extension[] parse(HttpMetaData metaData) {
62         assert(nextIncomingFrames !is null, "The next incoming frames MUST be not null");
63         assert(nextOutgoingFrames !is null, "The next outgoing frames MUST be not null");
64 
65         Extension[] extensions = _parse(metaData);
66         if (!extensions.empty) {
67             size_t len = extensions.length;
68             for (size_t i = 0; i < len; i++) {
69                 size_t next = i + 1;
70                 if (next < len - 1) {
71                     extensions[i].setNextIncomingFrames(extensions[next]);
72                 } else {
73                     extensions[i].setNextIncomingFrames(nextIncomingFrames);
74                 }
75             }
76             incomingFrames = extensions[0];
77 
78             for (size_t i = len - 1; i >= 0; i--) {
79                 size_t next = i - 1;
80                 if (next > 0) {
81                     extensions[i].setNextOutgoingFrames(extensions[next]);
82                 } else {
83                     extensions[i].setNextOutgoingFrames(nextOutgoingFrames);
84                 }
85             }
86             outgoingFrames = extensions[len - 1];
87             return extensions;
88         } else {
89             incomingFrames = nextIncomingFrames;
90             outgoingFrames = nextOutgoingFrames;
91             return [];
92         }
93     }
94 
95     protected Extension[] _parse(HttpMetaData metaData) {
96 
97         InputRange!string fieldValues = metaData.getFields().getValues(HttpHeader.SEC_WEBSOCKET_EXTENSIONS.asString());
98         Array!(ExtensionConfig) configList = ExtensionConfig.parseEnum(fieldValues);
99         auto r = configList[].filter!(c => factory.isAvailable(c.getName()))
100             .map!(delegate Extension (ExtensionConfig c) {
101                 Extension e = factory.newInstance(c);
102                 AbstractExtension abstractExtension = cast(AbstractExtension) e;
103                 if (abstractExtension !is null) 
104                     abstractExtension.setConfig(c);
105                 return e;
106             });
107 
108         return r.array;
109     }
110 
111     IncomingFrames getNextIncomingFrames() {
112         return nextIncomingFrames;
113     }
114 
115     void setNextIncomingFrames(IncomingFrames nextIncomingFrames) {
116         this.nextIncomingFrames = nextIncomingFrames;
117     }
118 
119     OutgoingFrames getNextOutgoingFrames() {
120         return nextOutgoingFrames;
121     }
122 
123     void setNextOutgoingFrames(OutgoingFrames nextOutgoingFrames) {
124         this.nextOutgoingFrames = nextOutgoingFrames;
125     }
126 
127     IncomingFrames getIncomingFrames() {
128         return incomingFrames;
129     }
130 
131     OutgoingFrames getOutgoingFrames() {
132         return outgoingFrames;
133     }
134 
135 }