1
2
3
4
5
6 using System;
7 using System.IO;
8 using System.Collections;
9
10 namespace System.Net.Http
11 {
12 public class HttpException : Exception
13 {
14 public HttpException(const HttpStatus& httpStatus_) : base(httpStatus_.ToString()), httpStatus(httpStatus_)
15 {
16 }
17 public nothrow const HttpStatus& Status() const
18 {
19 return httpStatus;
20 }
21 private HttpStatus httpStatus;
22 }
23
24 public class HttpVersion
25 {
26 public nothrow HttpVersion(const string& version_) : version(version_)
27 {
28 }
29 public virtual default ~HttpVersion();
30 public inline nothrow const string& Version() const
31 {
32 return version;
33 }
34 private string version;
35 }
36
37 public class Http_1_1_Version : HttpVersion
38 {
39 public nothrow Http_1_1_Version() : base("HTTP/1.1")
40 {
41 }
42 }
43
44 public class HttpStatus
45 {
46 public nothrow HttpStatus()
47 {
48 }
49 public nothrow HttpStatus(const string& httpVersion_, ushort statusCode_, const string& reasonPhrase_) :
50 httpVersion(httpVersion_), statusCode(statusCode_), reasonPhrase(reasonPhrase_)
51 {
52 }
53 public inline nothrow const string& HttpVersion() const
54 {
55 return httpVersion;
56 }
57 public inline nothrow ushort StatusCode() const
58 {
59 return statusCode;
60 }
61 public inline nothrow const string& ReasonPhrase() const
62 {
63 return reasonPhrase;
64 }
65 public string ToString() const
66 {
67 return httpVersion + " " + ToString(statusCode) + " " + reasonPhrase;
68 }
69 private string httpVersion;
70 private ushort statusCode;
71 private string reasonPhrase;
72 }
73
74
75
76 public const ushort statusInfoStart = 100u;
77 public const ushort statusInfoEnd = 199u;
78 public const ushort statusInfoContinue = 100u;
79 public const ushort statusInfoSwitchingProtocols = 101u;
80
81 public const ushort statusSuccessStart = 200u;
82 public const ushort statusSuccessEnd = 299u;
83 public const ushort statusSuccessOK = 200u;
84 public const ushort statusSuccessCreated = 201u;
85 public const ushort statusSuccessAccepted = 202u;
86 public const ushort statusSuccessNonAuthoritativeInformation = 203u;
87 public const ushort statusSuccessNoContent = 204u;
88 public const ushort statusSuccessResetContent = 205u;
89 public const ushort statusSuccessPartialContent = 206u;
90
91 public const ushort statusRedirectionStart = 300u;
92 public const ushort statusRedirectionEnd = 399u;
93 public const ushort statusRedirectionMultipleChoices = 300u;
94 public const ushort statusRedirectionMovedPermanently = 301u;
95 public const ushort statusRedirectionFound = 302u;
96 public const ushort statusRedirectionSeeOther = 303u;
97 public const ushort statusRedirectionNotModified = 304u;
98 public const ushort statusRedirectionUseProxy = 305u;
99 public const ushort statusRedirectionUnused = 306u;
100 public const ushort statusRedirectionTemporaryRedirect = 307u;
101
102 public const ushort statusClientErrorStart = 400u;
103 public const ushort statusClientErrorEnd = 499u;
104 public const ushort statusClientErrorBadRequest = 400u;
105 public const ushort statusClientErrorUnauthorized = 401u;
106 public const ushort statusClientErrorPaymentRequired = 402u;
107 public const ushort statusClientErrorForbidden = 403u;
108 public const ushort statusClientErrorNotFound = 404u;
109 public const ushort statusClientErrorMethodNotAllowed = 405u;
110 public const ushort statusClientErrorNotAcceptable = 406u;
111 public const ushort statusClientErrorProxyAuthenticationRequired = 407u;
112 public const ushort statusClientErrorRequestTimeout = 408u;
113 public const ushort statusClientErrorConflict = 409u;
114 public const ushort statusClientErrorGone = 410u;
115 public const ushort statusClientErrorLengthRequired = 411u;
116 public const ushort statusClientErrorPreconditionFailed = 412u;
117 public const ushort statusClientErrorRequestEntityTooLarge = 413u;
118 public const ushort statusClientErrorRequestURITooLong = 414u;
119 public const ushort statusClientErrorUnsupportedMediaType = 415u;
120 public const ushort statusClientErrorRequestedRangeNotSatisfiable = 416u;
121 public const ushort statusClientErrorExpectationFailed = 417u;
122
123 public const ushort statusServerErrorStart = 500u;
124 public const ushort statusServerErrorEnd = 599u;
125 public const ushort statusServerInternalServerError = 500u;
126 public const ushort statusServerNotImplemented = 501u;
127 public const ushort statusServerBadGateway = 502u;
128 public const ushort statusServerServiceUnavailable = 503u;
129 public const ushort statusServerGatewayTimeout = 504u;
130 public const ushort statusServerHttpVersionNotSupported = 505u;
131
132 public class HttpFieldValue
133 {
134 public HttpFieldValue() : fieldValue()
135 {
136 }
137 public default HttpFieldValue(const HttpFieldValue&);
138 public default void operator=(const HttpFieldValue&);
139 public HttpFieldValue(const string& fieldValue_) : fieldValue(fieldValue_)
140 {
141 }
142 public inline nothrow const string& FieldValue() const
143 {
144 return fieldValue;
145 }
146 public string ToString() const
147 {
148 string value = fieldValue;
149 for (const Pair<string, string>& p : parameters)
150 {
151 value.Append(';').Append(p.first);
152 if (!p.second.IsEmpty())
153 {
154 value.Append('=');
155 string paramValue = p.second;
156 if (paramValue.Find('"') != -1 || paramValue.Find(' ') != -1)
157 {
158 paramValue = MakeStringLiteral(paramValue);
159 }
160 value.Append(paramValue);
161 }
162 }
163 return value;
164 }
165 public void SetFieldValue(const string& fieldValue_)
166 {
167 fieldValue = fieldValue_;
168 }
169 public void SetParameter(const string& paramName, const string& paramValue)
170 {
171 parameters[ToLower(paramName)] = paramValue;
172 }
173 public string GetParameter(const string& paramName) const
174 {
175 string pn = ToLower(paramName);
176 Map<string, string>.ConstIterator it = parameters.CFind(pn);
177 if (it != parameters.CEnd())
178 {
179 return it->second;
180 }
181 return string();
182 }
183 private string fieldValue;
184 private Map<string, string> parameters;
185 }
186
187 public class HttpHeader
188 {
189 public nothrow HttpHeader()
190 {
191 }
192 public HttpHeader(const string& fieldName_, const string& fieldValue_) :
193 fieldName(fieldName_), fieldValueList(1, HttpFieldValue(Trim(fieldValue_))), combineWithSpace(false)
194 {
195 }
196 public HttpHeader(const string& fieldName_, HttpFieldValue&& fieldValue_) :
197 fieldName(fieldName_), fieldValueList(), combineWithSpace(false)
198 {
199 fieldValueList.Add(fieldValue_);
200 }
201 public HttpHeader(const string& fieldName_, List<HttpFieldValue>&& fieldValueList_) :
202 fieldName(fieldName_), fieldValueList(fieldValueList_), combineWithSpace(false)
203 {
204 }
205 public HttpHeader(const string& fieldName_, List<HttpFieldValue>&& fieldValueList_, bool combineWithSpace_) :
206 fieldName(fieldName_), fieldValueList(fieldValueList_), combineWithSpace(combineWithSpace_)
207 {
208 }
209 public virtual default ~HttpHeader();
210 public string CombinedFieldValue() const
211 {
212 string combinedFieldValue;
213 bool first = true;
214 for (const HttpFieldValue& fieldValue : fieldValueList)
215 {
216 if (first)
217 {
218 first = false;
219 }
220 else
221 {
222 if (combineWithSpace)
223 {
224 combinedFieldValue.Append(' ');
225 }
226 else
227 {
228 combinedFieldValue.Append(", ");
229 }
230 }
231 combinedFieldValue.Append(fieldValue.ToString());
232 }
233 return combinedFieldValue;
234 }
235 public string ToString() const
236 {
237 return fieldName + ": " + CombinedFieldValue();
238 }
239 public string FieldName() const
240 {
241 return ToLower(fieldName);
242 }
243 public inline const HttpFieldValue& SingleFieldValue() const
244 {
245 if (fieldValueList.IsEmpty())
246 {
247 ThrowPreconditionViolationException();
248 }
249 return fieldValueList.Front();
250 }
251 public void AddFieldValue(const HttpFieldValue& fieldValue)
252 {
253 fieldValueList.Add(fieldValue);
254 }
255 public void SetCombineWithSpace()
256 {
257 combineWithSpace = true;
258 }
259 private string fieldName;
260 private List<HttpFieldValue> fieldValueList;
261 private bool combineWithSpace;
262 }
263
264 public class HttpHeaderCollection
265 {
266 public void Add(UniquePtr<HttpHeader>&& header)
267 {
268 HttpHeader* prev = GetHeader(header->FieldName());
269 if (prev != null)
270 {
271 prev->AddFieldValue(header->SingleFieldValue());
272 }
273 else
274 {
275 headerMap[header->FieldName()] = header.Get();
276 headers.Add(header);
277 }
278 }
279 public HttpHeader* GetHeader(const string& fieldName) const
280 {
281 HashMap<string, HttpHeader*>.ConstIterator it = headerMap.CFind(ToLower(fieldName));
282 if (it != headerMap.CEnd())
283 {
284 return it->second;
285 }
286 return null;
287 }
288 public inline nothrow const List<UniquePtr<HttpHeader>>& Headers() const
289 {
290 return headers;
291 }
292 public nothrow void Clear()
293 {
294 headers.Clear();
295 headerMap.Clear();
296 }
297 public ulong GetContentLength() const
298 {
299 HttpHeader* contentLengthHeader = GetHeader("content-length");
300 if (contentLengthHeader != null)
301 {
302 string contentLength = contentLengthHeader->SingleFieldValue().FieldValue();
303 if (!contentLength.IsEmpty())
304 {
305 return ParseULong(contentLength);
306 }
307 }
308 return 0u;
309 }
310 public MimeType GetContentType() const
311 {
312 HttpHeader* contentTypeHeader = GetHeader("content-type");
313 if (contentTypeHeader != null)
314 {
315 string fieldValue = contentTypeHeader->SingleFieldValue().FieldValue();
316 MimeType mimeType = HttpParser.ParseMediaType(fieldValue);
317 return mimeType;
318 }
319 return MimeType();
320 }
321 public DateTime GetDate() const
322 {
323 HttpHeader* dateHeader = GetHeader("date");
324 if (dateHeader != null)
325 {
326 string fieldValue = dateHeader->SingleFieldValue().FieldValue();
327 DateTime date = HttpParser.ParseDate(fieldValue);
328 return date;
329 }
330 return DateTime();
331 }
332 private List<UniquePtr<HttpHeader>> headers;
333 private HashMap<string, HttpHeader*> headerMap;
334 }
335
336 public string MakeHttpHostValue(const string& host, int port)
337 {
338 if (port == -1)
339 {
340 port = 80;
341 }
342 if (port != 80)
343 {
344 return host + ":" + ToString(port);
345 }
346 else
347 {
348 return host;
349 }
350 }
351
352 public class HttpHostHeader : HttpHeader
353 {
354 public HttpHostHeader(const string& host_) : this(host_, 80)
355 {
356 }
357 public HttpHostHeader(const string& host_, int port_) : base("Host", MakeHttpHostValue(host_, port_))
358 {
359 }
360 }
361
362 public class HttpContentLengthHeader : HttpHeader
363 {
364 public HttpContentLengthHeader(int contentLength_) : base("Content-Length", ToString(contentLength_))
365 {
366 }
367 }
368
369 public List<HttpFieldValue> MakeProductListValue(const List<string>& products)
370 {
371 List<HttpFieldValue> fieldValueList;
372 for (const string& product : products)
373 {
374 fieldValueList.Add(HttpFieldValue(product));
375 }
376 return fieldValueList;
377 }
378
379 public class HttpUserAgentHeader : HttpHeader
380 {
381 public HttpUserAgentHeader() : this("Cmajor-http-client/3.3.0")
382 {
383 }
384 public HttpUserAgentHeader(const string& product_) : base("User-Agent", product_)
385 {
386 }
387 public HttpUserAgentHeader(const List<string>& products_) : base("User-Agent", MakeProductListValue(products_), true)
388 {
389 }
390 }
391
392 public List<HttpFieldValue> MakeMediaRangeListValue(const List<MediaRange>& mediaRanges)
393 {
394 List<HttpFieldValue> fieldValueList;
395 for (const MediaRange& mediaRange : mediaRanges)
396 {
397 fieldValueList.Add(mediaRange.ToHttpFieldValue());
398 }
399 return fieldValueList;
400 }
401
402 public class HttpAcceptHeader : HttpHeader
403 {
404 public HttpAcceptHeader(const MediaRange& mediaRange) : base("Accept", mediaRange.ToHttpFieldValue())
405 {
406 }
407 public HttpAcceptHeader(const List<MediaRange>& mediaRanges) : base("Accept", MakeMediaRangeListValue(mediaRanges))
408 {
409 }
410 }
411
412 public class Coding
413 {
414 public nothrow Coding(const string& coding_) : coding(coding_), quality(1)
415 {
416 }
417 public nothrow Coding(const string& coding_, double quality_) : coding(coding_), quality(quality_)
418 {
419 }
420 public virtual default ~Coding();
421 public HttpFieldValue ToHttpFieldValue() const
422 {
423 if (quality == 1)
424 {
425 return HttpFieldValue(coding);
426 }
427 else
428 {
429 HttpFieldValue result(coding);
430 result.SetParameter("q", ToString(quality, 3));
431 return result;
432 }
433 }
434 private string coding;
435 private double quality;
436 }
437
438 public class ChunkedTransferCoding : Coding
439 {
440 public nothrow ChunkedTransferCoding() : base("chunked")
441 {
442 }
443 }
444
445 public class DeflateCoding : Coding
446 {
447 public nothrow DeflateCoding() : base("deflate")
448 {
449 }
450 public nothrow DeflateCoding(double quality) : base("deflate", quality)
451 {
452 }
453 }
454
455 public List<HttpFieldValue> MakeCodingListValue(const List<Coding>& codings)
456 {
457 List<HttpFieldValue> fieldValueList;
458 for (const Coding& coding : codings)
459 {
460 fieldValueList.Add(coding.ToHttpFieldValue());
461 }
462 return fieldValueList;
463 }
464
465 public class HttpTEHeader : HttpHeader
466 {
467 public HttpTEHeader(const Coding& transferCoding) : base("TE", transferCoding.ToHttpFieldValue())
468 {
469 }
470 public HttpTEHeader(const List<Coding>& transferCodings) : base("TE", MakeCodingListValue(transferCodings))
471 {
472 }
473 }
474
475 public class HttpAcceptEncodingHeader : HttpHeader
476 {
477 public HttpAcceptEncodingHeader(const Coding& encoding) : base("Accept-Encoding", encoding.ToHttpFieldValue())
478 {
479 }
480 public HttpAcceptEncodingHeader(const List<Coding>& encodings) : base("Accept-Encoding", MakeCodingListValue(encodings))
481 {
482 }
483 }
484
485 public class HttpMethod
486 {
487 public nothrow HttpMethod(const string& methodName_) : methodName(methodName_)
488 {
489 }
490 public virtual default ~HttpMethod();
491 public inline nothrow const string& MethodName() const
492 {
493 return methodName;
494 }
495 private string methodName;
496 }
497
498 public class HttpOptionsMethod : HttpMethod
499 {
500 public nothrow HttpOptionsMethod() : base("OPTIONS")
501 {
502 }
503 }
504
505 public class HttpGetMethod : HttpMethod
506 {
507 public nothrow HttpGetMethod() : base("GET")
508 {
509 }
510 }
511
512 public class HttpHeadMethod : HttpMethod
513 {
514 public nothrow HttpHeadMethod() : base("HEAD")
515 {
516 }
517 }
518
519 public class HttpPostMethod : HttpMethod
520 {
521 public nothrow HttpPostMethod() : base("POST")
522 {
523 }
524 }
525
526 public class HttpPutMethod : HttpMethod
527 {
528 public nothrow HttpPutMethod() : base("PUT")
529 {
530 }
531 }
532
533 public class HttpDeleteMethod : HttpMethod
534 {
535 public nothrow HttpDeleteMethod() : base("DELETE")
536 {
537 }
538 }
539
540 public class HttpTraceMethod : HttpMethod
541 {
542 public nothrow HttpTraceMethod() : base("TRACE")
543 {
544 }
545 }
546
547 public class HttpConnectMethod : HttpMethod
548 {
549 public nothrow HttpConnectMethod() : base("CONNECT")
550 {
551 }
552 }
553
554 public class HttpRequest
555 {
556 public HttpRequest(const UriReference& absoluteUri_, HttpHeaderCollection& headers_) :
557 this(UniquePtr<HttpMethod>(new HttpGetMethod()), absoluteUri_, UniquePtr<HttpVersion>(new Http_1_1_Version()), headers_)
558 {
559 }
560 public HttpRequest(UniquePtr<HttpMethod>&& method_, const UriReference& absoluteUri_, UniquePtr<HttpVersion>&& httpVersion_, HttpHeaderCollection& headers_) :
561 method(method_), absoluteUri(absoluteUri_), httpVersion(httpVersion_), headers(headers_)
562 {
563 }
564 public void Write(StreamWriter& writer)
565 {
566 Write(writer, null);
567 }
568 public void Write(StreamWriter& writer, StreamWriter* log)
569 {
570 string requestLine = method->MethodName();
571 UriReference requestUri;
572 requestUri.SetPath(absoluteUri.Path());
573 requestLine.Append(' ');
574 requestLine.Append(requestUri.ToString());
575 requestLine.Append(' ');
576 requestLine.Append(httpVersion->Version());
577 if (log != null)
578 {
579 log->WriteLine("REQUEST:");
580 log->WriteLine(requestLine);
581 }
582 writer.Write(requestLine);
583 writer.Write("\r\n");
584 HttpHeader* hostHeader = headers.GetHeader("host");
585 if (hostHeader == null)
586 {
587 headers.Add(UniquePtr<HttpHeader>(new HttpHostHeader(absoluteUri.GetAuthority().Host(), absoluteUri.GetAuthority().Port())));
588 }
589 HttpHeader* userAgentHeader = headers.GetHeader("user-agent");
590 if (userAgentHeader == null)
591 {
592 headers.Add(UniquePtr<HttpHeader>(new HttpUserAgentHeader()));
593 }
594 for (const UniquePtr<HttpHeader>& header : headers.Headers())
595 {
596 string headerStr = header->ToString();
597 if (log != null)
598 {
599 log->WriteLine(headerStr);
600 }
601 writer.Write(headerStr);
602 writer.Write("\r\n");
603 }
604 writer.Write("\r\n");
605 writer.ContainedStream()->Flush();
606 }
607 private UniquePtr<HttpMethod> method;
608 private UriReference absoluteUri;
609 private UniquePtr<HttpVersion> httpVersion;
610 private HttpHeaderCollection& headers;
611 }
612 }