1 /*
2  * Collie - An asynchronous event-driven network framework using Dlang development
3  *
4  * Copyright (C) 2015-2017  Shanghai Putao Technology Co., Ltd 
5  *
6  * Developer: putao's Dlang team
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 module collie.codec.http.codec.httpcodec;
12 
13 public import collie.codec.http.httpmessage;
14 public import collie.codec.http.errocode;
15 public import collie.codec.http.codec.wsframe;
16 public import collie.codec.http.httptansaction;
17 public import collie.codec.http.httpwritebuffer;
18 import kiss.container.Vector;
19 
20 import std.experimental.allocator.gc_allocator;
21 
22 enum CodecProtocol : ubyte {
23 	init = 0,
24 	HTTP_1_X = 1,
25 	WEBSOCKET = 2,
26 //	SPDY_3,
27 //	SPDY_3_1,
28 //	SPDY_3_1_HPACK,
29 	HTTP_2 = 3
30 };
31 
32 abstract class HTTPCodec
33 {
34 
35 	/**
36    * Key that uniquely identifies a request/response pair within
37    * (and only within) the scope of the codec.  Code outside the
38    * codec should regard the StreamID as an opaque data
39    * structure; different subclasses of HTTPCodec are likely to
40    * use different conventions for generating StreamID values.
41    *
42    * A value of zero indicates an uninitialized/unknown/unspecified
43    * StreamID.
44    */
45 	alias StreamID = uint;
46 
47 	this()
48 	{}
49 
50 	interface CallBack
51 	{
52 		/**
53      * Called when a new message is seen while parsing the ingress
54      * @param stream   The stream ID
55      * @param msg      A newly allocated HTTPMessage
56      */
57 	 void onMessageBegin(HTTPTransaction txn, HTTPMessage msg);
58 		
59 		/**
60      * Called when a new push message is seen while parsing the ingress.
61      *
62      * @param stream   The stream ID
63      * @param assocStream The stream ID of the associated stream,
64      *                 which can never be 0
65      * @param msg      A newly allocated HTTPMessage
66      */
67 //	void onPushMessageBegin(HTTPTransaction txn,
68 //		StreamID assocStream,
69 //			HTTPMessage* msg);
70 //		
71 		/**
72      * Called when all the headers of an ingress message have been parsed
73      * @param stream   The stream ID
74      * @param msg      The message
75      * @param size     Size of the ingress header
76      */
77 	void onHeadersComplete(HTTPTransaction txn,
78 		HTTPMessage msg);
79 		
80 		/**
81      * Called for each block of message body data
82      * @param stream  The stream ID
83      * @param chain   One or more buffers of body data. The codec will
84      *                remove any protocol framing, such as HTTP/1.1 chunk
85      *                headers, from the buffers before calling this function.
86      * @param padding Number of pad bytes that came with the data segment
87      */
88 		void onBody(HTTPTransaction txn,const ubyte[] data);
89 		
90 		/**
91      * Called for each HTTP chunk header.
92      *
93      * onChunkHeader() will be called when the chunk header is received.  As
94      * the chunk data arrives, it will be passed to the callback normally with
95      * onBody() calls.  Note that the chunk data may arrive in multiple
96      * onBody() calls: it is not guaranteed to arrive in a single onBody()
97      * call.
98      *
99      * After the chunk data has been received and the terminating CRLF has been
100      * received, onChunkComplete() will be called.
101      *
102      * @param stream    The stream ID
103      * @param length    The chunk length.
104      */
105 		void onChunkHeader(HTTPTransaction txn, size_t length);
106 		
107 		/**
108      * Called when the terminating CRLF is received to end a chunk of HTTP body
109      * data.
110      *
111      * @param stream    The stream ID
112      */
113 		void onChunkComplete(HTTPTransaction txn);
114 		
115 		/**
116      * Called at end of a message (including body and trailers, if applicable)
117      * @param stream   The stream ID
118      * @param upgrade  Whether the connection has been upgraded to another
119      *                 protocol.
120      */
121 		void onMessageComplete(HTTPTransaction txn, bool upgrade);
122 		
123 		/**
124      * Called when a parsing or protocol error has occurred
125      * @param stream   The stream ID
126      * @param error    Description of the error
127      * @param newTxn   true if onMessageBegin has not been called for txn
128      */
129 		void onError(HTTPTransaction txn,HTTPErrorCode);
130 		
131 		/**
132      * Called when the peer has asked to shut down a stream
133      * immediately.
134      * @param stream   The stream ID
135      * @param code     The code the stream was aborted with
136      * @note  Not applicable to all protocols.
137      */
138 		void onAbort(HTTPTransaction txn,
139 			HTTPErrorCode code);
140 
141 		void onWsFrame(HTTPTransaction ,ref WSFrame);
142 		/**
143      * Called upon receipt of a frame header.
144      * @param stream_id The stream ID
145      * @param flags     The flags field of frame header
146      * @param length    The length field of frame header
147      * @param version   The version of frame (SPDY only)
148      * @note Not all protocols have frames. SPDY does, but HTTP/1.1 doesn't.
149      */
150 //		void onFrameHeader(uint stream_id,
151 //			ubyte flags,
152 //			uint length,
153 //			ushort version_ = 0);
154 		
155 		/**
156      * Called upon receipt of a goaway.
157      * @param lastGoodStreamID  Last successful stream created by the receiver
158      * @param code              The code the connection was aborted with
159      * @param debugData         The additional debug data for diagnostic purpose
160      * @note Not all protocols have goaways. SPDY does, but HTTP/1.1 doesn't.
161      */
162 //		void onGoaway(ulong lastGoodStreamID,
163 //			HTTPErrorCode code,
164 //			const ubyte[] debugData = null);
165 
166 		/**
167      * Called upon receipt of a ping request
168      * @param uniqueID  Unique identifier for the ping
169      * @note Not all protocols have pings.  SPDY does, but HTTP/1.1 doesn't.
170      */
171 //		void onPingRequest(ulong uniqueID);
172 		
173 		/**
174      * Called upon receipt of a ping reply
175      * @param uniqueID  Unique identifier for the ping
176      * @note Not all protocols have pings.  SPDY does, but HTTP/1.1 doesn't.
177      */
178 //		void onPingReply(ulong uniqueID);
179 		
180 		/**
181      * Called upon receipt of a window update, for protocols that support
182      * flow control. For instance spdy/3 and higher.
183      */
184 //		void onWindowUpdate(HTTPTransaction txn, uint amount);
185 		
186 		/**
187      * Called upon receipt of a settings frame, for protocols that support
188      * settings.
189      *
190      * @param settings a list of settings that were sent in the settings frame
191      */
192 		//void onSettings(const SettingsList& settings);
193 		
194 		/**
195      * Called upon receipt of a settings frame with ACK set, for
196      * protocols that support settings ack.
197      */
198 //		void onSettingsAck();
199 		
200 		/**
201      * Called upon receipt of a priority frame, for protocols that support
202      * dynamic priority
203      */
204 //		void onPriority(HTTPTransaction txn,
205 //			const HTTPMessage::HTTPPriority& pri);
206 		
207 		/**
208      * Called upon receipt of a valid protocol switch.  Return false if
209      * protocol switch could not be completed.
210      */
211 		void onNativeProtocolUpgrade(HTTPTransaction txn,
212 			CodecProtocol protocol,
213 			string protocolString,
214 			HTTPMessage msg);
215 		/**
216      * Return the number of open streams started by this codec callback.
217      * Parallel codecs with a maximum number of streams will invoke this
218      * to determine if a new stream exceeds the limit.
219      */
220 		//uint32_t numOutgoingStreams() const { return 0; }
221 		
222 		/**
223      * Return the number of open streams started by the remote side.
224      * Parallel codecs with a maximum number of streams will invoke this
225      * to determine if a new stream exceeds the limit.
226      */
227 		//uint32_t numIncomingStreams() const { return 0; }
228 
229 
230 	}
231 
232 	CodecProtocol getProtocol();
233 
234 	StreamID createStream();
235 	/**
236    * Get the transport direction of this codec:
237    * DOWNSTREAM if the codec receives requests from clients or
238    * UPSTREAM if the codec sends requests to servers.
239    */
240 	TransportDirection getTransportDirection();
241 	
242 	/**
243    * Returns true iff this codec supports per stream flow control
244    */
245 	bool supportsStreamFlowControl() const {
246 		return false;
247 	}
248 	
249 	/**
250    * Returns true iff this codec supports session level flow control
251    */
252 	bool supportsSessionFlowControl() const {
253 		return false;
254 	}
255 	
256 	/**
257    * Set the callback to notify on ingress events
258    * @param callback  The callback object
259    */
260 	void setCallback(CallBack callback);
261 	
262 	/**
263    * Check whether the codec still has at least one HTTP
264    * stream to parse.
265    */
266 	bool isBusy();
267 
268 	bool shouldClose();
269 	
270 	/**
271    * Pause or resume the ingress parser
272    * @param paused  Whether the caller wants the parser to be paused
273    */
274 	void setParserPaused(bool paused);
275 	
276 	/**
277    * Parse ingress data.
278    * @param  buf   A single IOBuf of data to parse
279    * @return Number of bytes consumed.
280    */
281 	size_t onIngress(ubyte[] buf);
282 
283 	void onConnectClose();
284 
285 	void onTimeOut();
286 
287 	void detach(HTTPTransaction txn);
288 	/**
289    * Finish parsing when the ingress stream has ended.
290    */
291 
292 	//void onIngressEOF();
293 	
294 /**
295    * Invoked on a codec that has been upgraded to via an HTTPMessage on
296    * a different codec.  The codec may return false to halt the upgrade.
297    */
298 //	bool onIngressUpgradeMessage(const HTTPMessage msg) {
299 //		return true;
300 //	}
301 	
302 	/**
303    * Check whether the codec can process new streams. Typically,
304    * an implementing subclass will return true when a new codec is
305    * created and false once it encounters a situation that would
306    * prevent reuse of the underlying transport (e.g., a "Connection: close"
307    * in HTTP/1.x).
308    * @note A return value of true means that the codec can process new
309    *       connections at some reasonable point in the future; that may
310    *       mean "immediately," for codecs that support pipelined or
311    *       interleaved requests, or "upon completion of the current
312    *       stream" for codecs that do not.
313    */
314 //	bool isReusable();
315 	
316 	/**
317    * Returns true if this codec is in a state where it accepting new
318    * requests but will soon begin to reject new requests. For SPDY and
319    * HTTP/2, this is true when the first GOAWAY NO_ERROR is sent during
320    * graceful shutdown.
321    */
322 //	bool isWaitingToDrain();
323 	
324 	/**
325    * Checks whether the socket needs to be closed when EOM is sent. This is used
326    * during CONNECT when EOF needs to be sent after upgrade to notify the server
327    */
328 //	bool closeOnEgressComplete();
329 	
330 	/**
331    * Check whether the codec supports the processing of multiple
332    * requests in parallel.
333    */
334 //	bool supportsParallelRequests();
335 	
336 	/**
337    * Check whether the codec supports pushing resources from server to
338    * client.
339    */
340 //	bool supportsPushTransactions();
341 	
342 	/**
343    * Write an egress message header.  For pushed streams, you must specify
344    * the assocStream.
345    * @retval size the size of the generated message, both the actual size
346    *              and the size of the uncompressed data.
347    * @return None
348    */
349 	size_t generateHeader(
350 		HTTPTransaction txn,
351 		HTTPMessage msg,
352 		HttpWriteBuffer buffer,
353 		bool eom = false);
354 		//HTTPHeaderSize* size = nullptr);
355 	
356 	/**
357    * Write part of an egress message body.
358    *
359    * This will automatically generate a chunk header and footer around the data
360    * if necessary (e.g. you haven't manually sent a chunk header and the
361    * message should be chunked).
362    *
363    * @param padding Optionally add padding bytes to the body if possible
364    * @param eom implicitly generate the EOM marker with this body frame
365    *
366    * @return number of bytes written
367    */
368 	size_t generateBody(HTTPTransaction txn,
369 		HttpWriteBuffer chain,in ubyte[] data,
370 		bool eom);
371 
372 	/**
373    * Write a body chunk header, if relevant.
374    */
375 	size_t generateChunkHeader(
376 		HTTPTransaction txn,
377 		HttpWriteBuffer buffer,
378 		size_t length);
379 	
380 	/**
381    * Write a body chunk terminator, if relevant.
382    */
383 	size_t generateChunkTerminator(
384 		HTTPTransaction txn,
385 		HttpWriteBuffer buffer);
386 
387 	/**
388    * Generate any protocol framing needed to finalize an egress
389    * message. This method must be called to complete a stream.
390    *
391    * @return number of bytes written
392    */
393 	size_t generateEOM(HTTPTransaction txn,
394 		HttpWriteBuffer buffer);
395 	
396 	/**
397    * Generate any protocol framing needed to abort a connection.
398    * @return number of bytes written
399    */
400 	size_t generateRstStream(HTTPTransaction txn,
401 		HttpWriteBuffer buffer,HTTPErrorCode code);
402 
403 
404 	size_t generateWsFrame(HTTPTransaction txn,
405 		HttpWriteBuffer buffer,OpCode code, ubyte[] data)
406 	{
407 		return 0;
408 	}
409 
410 	static CodecProtocol getProtocolFormString(string str)
411 	{
412 		import collie.utils..string;
413 		if(isSameIngnoreLowUp(str,"websocket")){
414 			return CodecProtocol.WEBSOCKET;
415 		} else if(isSameIngnoreLowUp(str,"H2C")){
416 			return CodecProtocol.HTTP_2;
417 		}
418 		return CodecProtocol.init;
419 		/*
420 		switch(str){
421 
422 			case "websocket" : 
423 				return CodecProtocol.WEBSOCKET;
424 			case "H2C" :
425 				return CodecProtocol.HTTP_2;
426 			default:
427 				return CodecProtocol.init;
428 		}*/
429 	}
430 }
431