1 module hunt.http.server.Http2ServerRequestHandler;
2 
3 import hunt.http.server.HttpServerResponse;
4 import hunt.http.server.Http2ServerConnection;
5 import hunt.http.server.ServerHttpHandler;
6 import hunt.http.server.ServerSessionListener;
7 
8 import hunt.http.codec.http.frame;
9 import hunt.http.codec.http.model;
10 import hunt.http.codec.http.stream.AbstractHttp2OutputStream;
11 import hunt.http.codec.http.stream.Session;
12 import hunt.http.codec.http.stream.Stream;
13 
14 import hunt.http.codec.http.stream.DataFrameHandler;
15 import hunt.http.environment;
16 
17 import hunt.lang.exception;
18 import hunt.util.functional;
19 import hunt.string;
20 
21 import hunt.logging;
22 import std.conv;
23 import std.string;
24 
25 alias StreamListener = hunt.http.codec.http.stream.Stream.Stream.Listener;
26 
27 /**
28 */
29 class Http2ServerRequestHandler : ServerSessionListener.Adapter {
30 
31     private ServerHttpHandler serverHttpHandler;
32     Http2ServerConnection connection;
33 
34     this(ServerHttpHandler serverHttpHandler) {
35         this.serverHttpHandler = serverHttpHandler;
36     }
37 
38     override
39     void onClose(Session session, GoAwayFrame frame) {
40         warningf("Server received the GoAwayFrame -> %s", frame.toString());
41         connection.close();
42     }
43 
44     override
45     void onFailure(Session session, Exception failure) {
46         errorf("Server failure: " ~ session.toString(), failure);
47         // Optional.ofNullable(connection).ifPresent(IO::close);
48         if(connection !is null)
49             connection.close();
50     }
51 
52     override
53     void onReset(Session session, ResetFrame frame) {
54         warningf("Server received ResetFrame %s", frame.toString());
55         // Optional.ofNullable(connection).ifPresent(IO::close);
56 
57         if(connection !is null)
58             connection.close();
59     }
60 
61     override
62     StreamListener onNewStream(Stream stream, HeadersFrame headersFrame) {
63         if (!headersFrame.getMetaData().isRequest()) {
64             throw new IllegalArgumentException("the stream " ~ stream.getId().to!string() ~ " received meta data that is not request type");
65         }
66 
67         version(HUNT_DEBUG) {
68             tracef("Server received stream: %s, %s", stream.getId(), headersFrame.toString());
69         }
70 
71         HttpRequest request = cast(HttpRequest) headersFrame.getMetaData();
72         HttpResponse response = new HttpServerResponse();
73         ServerHttp2OutputStream output = new ServerHttp2OutputStream(response, stream);
74 
75         string expectedValue = request.getFields().get(HttpHeader.EXPECT);
76         if ("100-continue".equalsIgnoreCase(expectedValue)) {
77             bool skipNext = serverHttpHandler.accept100Continue(request, response, output, connection);
78             if (!skipNext) {
79                 HttpResponse continue100 = new HttpResponse(HttpVersion.HTTP_1_1,
80                         HttpStatus.CONTINUE_100, HttpStatus.Code.CONTINUE.getMessage(),
81                         new HttpFields(), -1);
82                 output.writeFrame(new HeadersFrame(stream.getId(), continue100, null, false));
83             }
84         } else {
85             serverHttpHandler.headerComplete(request, response, output, connection);
86             if (headersFrame.isEndStream()) {
87                 serverHttpHandler.messageComplete(request, response, output, connection);
88             }
89         }
90 
91         
92 
93         return new class StreamListener.Adapter {
94             override
95             void onHeaders(Stream stream, HeadersFrame trailerFrame) {
96                 version(HUNT_DEBUG) {
97                     tracef("Server received trailer frame: %s, %s", stream.toString(), trailerFrame);
98                 }
99 
100                 if (trailerFrame.isEndStream()) {
101                     request.setTrailerSupplier(() => trailerFrame.getMetaData().getFields());
102                     serverHttpHandler.contentComplete(request, response, output, connection);
103                     serverHttpHandler.messageComplete(request, response, output, connection);
104                 } else {
105                     throw new IllegalArgumentException("the stream " ~ stream.getId().to!string() ~ " received illegal meta data");
106                 }
107             }
108 
109             override
110             void onData(Stream stream, DataFrame dataFrame, Callback callback) {
111                 DataFrameHandler.handleDataFrame(dataFrame, callback, request, response, output, connection, serverHttpHandler);
112             }
113 
114             override
115             void onReset(Stream stream, ResetFrame resetFrame) {
116                 int errorCode = resetFrame.getError();
117                 string reason; 
118                 int status = HttpStatus.INTERNAL_SERVER_ERROR_500;
119                 if (isValidErrorCode(errorCode)) {
120                     switch (cast(ErrorCode)errorCode) {
121                         case ErrorCode.PROTOCOL_ERROR:
122                             status = HttpStatus.BAD_REQUEST_400;
123                             break;
124                         default:
125                             status = HttpStatus.INTERNAL_SERVER_ERROR_500;
126                             break;
127                     }
128                     reason =  (cast(ErrorCode)errorCode).to!string().toLower();
129                 }
130                 else
131                     reason =  "error=" ~ resetFrame.getError().to!string();
132 
133                 serverHttpHandler.badMessage(status, reason, request, response, output, connection);
134             }
135         };
136     }
137 
138     static class ServerHttp2OutputStream : AbstractHttp2OutputStream {
139 
140         enum string X_POWERED_BY_VALUE = "Hunt " ~ Version;
141         enum string SERVER_VALUE = "Hunt " ~ Version;
142 
143         private Stream stream;
144 
145         this(MetaData info, Stream stream) {
146             super(info, false);
147             this.stream = stream;
148             info.getFields().put(HttpHeader.X_POWERED_BY, X_POWERED_BY_VALUE);
149             info.getFields().put(HttpHeader.SERVER, SERVER_VALUE);
150         }
151 
152         override
153         protected Stream getStream() {
154             return stream;
155         }
156     }
157 
158 }