1 module hunt.http.codec.websocket.model.extension.compress.PerMessageDeflateExtension;
2 
3 // import hunt.http.Exceptions.BadPayloadException;
4 // import hunt.http.WebSocketFrame;
5 // import hunt.http.codec.websocket.model.ExtensionConfig;
6 // import hunt.http.WebSocketCommon;
7 // import hunt.util.Common;
8 // import hunt.logging;
9 
10 
11 // import hunt.io.ByteBuffer;
12 // import java.util.zip.DataFormatException;
13 
14 // /**
15 //  * Per Message Deflate Compression extension for WebSocket.
16 //  * <p>
17 //  * Attempts to follow <a href="https://tools.ietf.org/html/rfc7692">Compression Extensions for WebSocket</a>
18 //  */
19 // class PerMessageDeflateExtension : CompressExtension {
20 
21 
22 //     private ExtensionConfig configRequested;
23 //     private ExtensionConfig configNegotiated;
24 //     private bool incomingContextTakeover = true;
25 //     private bool outgoingContextTakeover = true;
26 //     private bool incomingCompressed;
27 
28 //     override
29 //     string getName() {
30 //         return "permessage-deflate";
31 //     }
32 
33 //     override
34 //     void incomingFrame(Frame frame) {
35 //         // Incoming frames are always non concurrent because
36 //         // they are read and parsed with a single thread, and
37 //         // therefore there is no need for synchronization.
38 
39 //         // This extension requires the RSV1 bit set only in the first frame.
40 //         // Subsequent continuation frames don't have RSV1 set, but are compressed.
41 //         if (frame.getType().isData()) {
42 //             incomingCompressed = frame.isRsv1();
43 //         }
44 
45 //         if (OpCode.isControlFrame(frame.getOpCode()) || !incomingCompressed) {
46 //             nextIncomingFrame(frame);
47 //             return;
48 //         }
49 
50 //         ByteAccumulator accumulator = newByteAccumulator();
51 
52 //         try {
53 //             ByteBuffer payload = frame.getPayload();
54 //             decompress(accumulator, payload);
55 //             if (frame.isFin()) {
56 //                 decompress(accumulator, TAIL_BYTES_BUF.slice());
57 //             }
58 
59 //             forwardIncoming(frame, accumulator);
60 //         } catch (DataFormatException e) {
61 //             throw new BadPayloadException(e);
62 //         }
63 
64 //         if (frame.isFin())
65 //             incomingCompressed = false;
66 //     }
67 
68 //     override
69 //     protected void nextIncomingFrame(Frame frame) {
70 //         if (frame.isFin() && !incomingContextTakeover) {
71 //             tracef("Incoming Context Reset");
72 //             decompressCount.set(0);
73 //             getInflater().reset();
74 //         }
75 //         super.nextIncomingFrame(frame);
76 //     }
77 
78 //     override
79 //     protected void nextOutgoingFrame(Frame frame, Callback callback) {
80 //         if (frame.isFin() && !outgoingContextTakeover) {
81 //             tracef("Outgoing Context Reset");
82 //             getDeflater().reset();
83 //         }
84 //         super.nextOutgoingFrame(frame, callback);
85 //     }
86 
87 //     override
88 //     int getRsvUseMode() {
89 //         return RSV_USE_ONLY_FIRST;
90 //     }
91 
92 //     override
93 //     int getTailDropMode() {
94 //         return TAIL_DROP_FIN_ONLY;
95 //     }
96 
97 //     override
98 //     void setConfig(final ExtensionConfig config) {
99 //         configRequested = new ExtensionConfig(config);
100 //         configNegotiated = new ExtensionConfig(config.getName());
101 
102 //         for (string key : config.getParameterKeys()) {
103 //             key = key.trim();
104 //             switch (key) {
105 //                 case "client_max_window_bits":
106 //                 case "server_max_window_bits": {
107 //                     // Don't negotiate these parameters
108 //                     break;
109 //                 }
110 //                 case "client_no_context_takeover": {
111 //                     configNegotiated.setParameter("client_no_context_takeover");
112 //                     switch (getPolicy().getBehavior()) {
113 //                         case CLIENT:
114 //                             incomingContextTakeover = false;
115 //                             break;
116 //                         case SERVER:
117 //                             outgoingContextTakeover = false;
118 //                             break;
119 //                     }
120 //                     break;
121 //                 }
122 //                 case "server_no_context_takeover": {
123 //                     configNegotiated.setParameter("server_no_context_takeover");
124 //                     switch (getPolicy().getBehavior()) {
125 //                         case CLIENT:
126 //                             outgoingContextTakeover = false;
127 //                             break;
128 //                         case SERVER:
129 //                             incomingContextTakeover = false;
130 //                             break;
131 //                     }
132 //                     break;
133 //                 }
134 //                 default: {
135 //                     throw new IllegalArgumentException();
136 //                 }
137 //             }
138 //         }
139 
140 //         tracef("config: outgoingContextTakover=%s, incomingContextTakeover=%s : %s", outgoingContextTakeover, incomingContextTakeover, this);
141 
142 //         super.setConfig(configNegotiated);
143 //     }
144 
145 //     override
146 //     string toString() {
147 //         return string.format("%s[requested=\"%s\", negotiated=\"%s\"]",
148 //                 typeof(this).stringof,
149 //                 configRequested.getParameterizedName(),
150 //                 configNegotiated.getParameterizedName());
151 //     }
152 // }