1 module hunt.http.client.Http1ClientResponseHandler;
2 
3 import hunt.http.client.ClientHttpHandler;
4 import hunt.http.client.Http1ClientConnection;
5 import hunt.http.client.HttpClientResponse;
6 
7 import hunt.http.codec.http.decode.HttpParser;
8 import hunt.http.codec.http.model;
9 import hunt.http.HttpOutputStream;
10 
11 import hunt.http.HttpHeader;
12 import hunt.http.HttpField;
13 import hunt.http.HttpFields;
14 import hunt.http.HttpRequest;
15 import hunt.http.HttpResponse;
16 import hunt.http.HttpStatus;
17 import hunt.http.HttpVersion;
18 
19 import hunt.io.ByteBuffer;
20 import hunt.stream;
21 import hunt.logging;
22 import hunt.text.Common;
23 
24 version(WITH_HUNT_TRACE) {
25     import hunt.trace.Constrants;
26     import hunt.trace.Span;
27 }
28 
29 import std.string : icmp;
30 import std.conv;
31 
32 
33 /**
34  * 
35  */
36 class Http1ClientResponseHandler : HttpResponseParsingHandler {
37     package(hunt.http.client)  Http1ClientConnection connection;
38     package(hunt.http.client)  HttpResponse response;
39     package(hunt.http.client)  HttpRequest request;
40     package(hunt.http.client)  HttpOutputStream outputStream;
41     protected ClientHttpHandler clientHttpHandler;
42     protected HttpFields trailer;
43 
44     this(ClientHttpHandler clientHttpHandler) {
45         this.clientHttpHandler = clientHttpHandler;
46 
47     }
48 
49     package(hunt.http.client) void onReady() {
50         // do nothing
51     }
52 
53     override
54     final bool startResponse(HttpVersion ver, int status, string reason) {
55         version(HUNT_HTTP_DEBUG) {
56             tracef("client received the response line, %s, %s, %s", ver, status, reason);
57         }
58 
59         if (status == HttpStatus.CONTINUE_100 && HttpStatus.Code.CONTINUE.getMessage().equalsIgnoreCase(reason)) {
60             clientHttpHandler.continueToSendData(request, response, outputStream, connection);
61             version(HUNT_HTTP_DEBUG) {
62                 tracef("client received 100 continue, current parser state is %s", connection.getParser().getState());
63             }
64             return true;
65         } else {
66             response = new HttpClientResponse(ver, status, reason);
67             return false;
68         }
69     }
70 
71     override
72     final void parsedHeader(HttpField field) {
73         response.getFields().add(field);
74     }
75 
76     override
77     final int getHeaderCacheSize() {
78         return 1024;
79     }
80 
81     override
82     final bool headerComplete() {
83         version(HUNT_HTTP_DEBUG_MORE) trace("handle response");
84         return clientHttpHandler.headerComplete(request, response, outputStream, connection);
85     }
86 
87     override
88     final bool content(ByteBuffer item) {
89         version(HUNT_HTTP_DEBUG_MORE) trace("handle response");
90         return clientHttpHandler.content(item, request, response, outputStream, connection);
91     }
92 
93     override
94     bool contentComplete() {
95         version(HUNT_HTTP_DEBUG_MORE) trace("handle response");
96         return clientHttpHandler.contentComplete(request, response, outputStream, connection);
97     }
98 
99     override
100     void parsedTrailer(HttpField field) {
101         if (trailer is null) {
102             trailer = new HttpFields();
103             response.setTrailerSupplier(() => trailer);
104         }
105         trailer.add(field);
106     }
107 
108     protected bool http1MessageComplete() {
109         version(HUNT_HTTP_DEBUG_MORE) trace("handle response");
110         try {
111             // version(WITH_HUNT_TRACE) endTraceSpan("");
112             return clientHttpHandler.messageComplete(request, response, outputStream, connection);
113         } finally {
114             string requestConnectionValue = request.getFields().get(HttpHeader.CONNECTION);
115             string responseConnectionValue = response.getFields().get(HttpHeader.CONNECTION);
116 
117             connection.getParser().reset();
118 
119             HttpVersion httpVersion = response.getHttpVersion();
120 
121             if(httpVersion == HttpVersion.HTTP_1_0) {
122                 if (icmp("keep-alive", requestConnectionValue)
123                         && icmp("keep-alive", responseConnectionValue)) {
124                     tracef("the client %s connection is persistent", response.getHttpVersion());
125                 } else {
126                     IOUtils.close(connection);
127                 } 
128             } else if (httpVersion == HttpVersion.HTTP_1_1){ // the persistent connection is default in HTTP 1.1
129                 if (icmp("close", requestConnectionValue)
130                         || icmp("close", responseConnectionValue)) {
131                     IOUtils.close(connection);
132                 } else {
133                     tracef("the client %s connection is persistent", response.getHttpVersion());
134                 }
135             }
136 
137         }
138     }
139 
140     override
141     final bool messageComplete() {
142         bool success = connection.upgradeProtocolComplete(request, response);
143         if (success) {
144             tracef("client upgraded protocol successfully");
145         }
146         return http1MessageComplete();
147     }
148 
149     
150     void badMessage(BadMessageException failure) {
151         badMessage(failure.getCode(), failure.getReason());
152     }
153 
154     override
155     final void badMessage(int status, string reason) {
156         clientHttpHandler.badMessage(status, reason, request, response, outputStream, connection);
157     }
158 
159     override
160     void earlyEOF() {
161         clientHttpHandler.earlyEOF(request, response, outputStream, connection);
162     }
163 
164 }