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