diff options
Diffstat (limited to 'lib/pure/httpcore.nim')
-rw-r--r-- | lib/pure/httpcore.nim | 146 |
1 files changed, 58 insertions, 88 deletions
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index 9287e9864..5ccab379c 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -7,12 +7,12 @@ # distribution, for details about the copyright. # -## Contains functionality shared between the ``httpclient`` and -## ``asynchttpserver`` modules. +## Contains functionality shared between the `httpclient` and +## `asynchttpserver` modules. ## ## Unstable API. import std/private/since -import tables, strutils, parseutils +import std/[tables, strutils, parseutils] type HttpHeaders* = ref object @@ -29,24 +29,26 @@ type HttpVer11, HttpVer10 - HttpMethod* = enum ## the requested HttpMethod - HttpHead, ## Asks for the response identical to the one that would - ## correspond to a GET request, but without the response - ## body. - HttpGet, ## Retrieves the specified resource. - HttpPost, ## Submits data to be processed to the identified - ## resource. The data is included in the body of the - ## request. - HttpPut, ## Uploads a representation of the specified resource. - HttpDelete, ## Deletes the specified resource. - HttpTrace, ## Echoes back the received request, so that a client - ## can see what intermediate servers are adding or - ## changing in the request. - HttpOptions, ## Returns the HTTP methods that the server supports - ## for specified address. - HttpConnect, ## Converts the request connection to a transparent - ## TCP/IP tunnel, usually used for proxies. - HttpPatch ## Applies partial modifications to a resource. + HttpMethod* = enum ## the requested HttpMethod + HttpHead = "HEAD" ## Asks for the response identical to the one that + ## would correspond to a GET request, but without + ## the response body. + HttpGet = "GET" ## Retrieves the specified resource. + HttpPost = "POST" ## Submits data to be processed to the identified + ## resource. The data is included in the body of + ## the request. + HttpPut = "PUT" ## Uploads a representation of the specified + ## resource. + HttpDelete = "DELETE" ## Deletes the specified resource. + HttpTrace = "TRACE" ## Echoes back the received request, so that a + ## client + ## can see what intermediate servers are adding or + ## changing in the request. + HttpOptions = "OPTIONS" ## Returns the HTTP methods that the server + ## supports for specified address. + HttpConnect = "CONNECT" ## Converts the request connection to a transparent + ## TCP/IP tunnel, usually used for proxies. + HttpPatch = "PATCH" ## Applies partial modifications to a resource. const @@ -124,23 +126,20 @@ func toTitleCase(s: string): string = result[i] = if upper: toUpperAscii(s[i]) else: toLowerAscii(s[i]) upper = s[i] == '-' -func toCaseInsensitive(headers: HttpHeaders, s: string): string {.inline.} = +func toCaseInsensitive*(headers: HttpHeaders, s: string): string {.inline.} = + ## For internal usage only. Do not use. return if headers.isTitleCase: toTitleCase(s) else: toLowerAscii(s) func newHttpHeaders*(titleCase=false): HttpHeaders = - ## Returns a new ``HttpHeaders`` object. if ``titleCase`` is set to true, + ## Returns a new `HttpHeaders` object. if `titleCase` is set to true, ## headers are passed to the server in title case (e.g. "Content-Length") - new result - result.table = newTable[string, seq[string]]() - result.isTitleCase = titleCase + result = HttpHeaders(table: newTable[string, seq[string]](), isTitleCase: titleCase) func newHttpHeaders*(keyValuePairs: openArray[tuple[key: string, val: string]], titleCase=false): HttpHeaders = - ## Returns a new ``HttpHeaders`` object from an array. if ``titleCase`` is set to true, + ## Returns a new `HttpHeaders` object from an array. if `titleCase` is set to true, ## headers are passed to the server in title case (e.g. "Content-Length") - new result - result.table = newTable[string, seq[string]]() - result.isTitleCase = titleCase + result = HttpHeaders(table: newTable[string, seq[string]](), isTitleCase: titleCase) for pair in keyValuePairs: let key = result.toCaseInsensitive(pair.key) @@ -150,7 +149,6 @@ func newHttpHeaders*(keyValuePairs: else: result.table[key] = @[pair.val] - func `$`*(headers: HttpHeaders): string {.inline.} = $headers.table @@ -158,35 +156,36 @@ proc clear*(headers: HttpHeaders) {.inline.} = headers.table.clear() func `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues = - ## Returns the values associated with the given ``key``. If the returned - ## values are passed to a procedure expecting a ``string``, the first + ## Returns the values associated with the given `key`. If the returned + ## values are passed to a procedure expecting a `string`, the first ## value is automatically picked. If there are ## no values associated with the key, an exception is raised. ## - ## To access multiple values of a key, use the overloaded ``[]`` below or - ## to get all of them access the ``table`` field directly. + ## To access multiple values of a key, use the overloaded `[]` below or + ## to get all of them access the `table` field directly. {.cast(noSideEffect).}: - return headers.table[headers.toCaseInsensitive(key)].HttpHeaderValues + let tmp = headers.table[headers.toCaseInsensitive(key)] + return HttpHeaderValues(tmp) converter toString*(values: HttpHeaderValues): string = return seq[string](values)[0] func `[]`*(headers: HttpHeaders, key: string, i: int): string = - ## Returns the ``i``'th value associated with the given key. If there are - ## no values associated with the key or the ``i``'th value doesn't exist, + ## Returns the `i`'th value associated with the given key. If there are + ## no values associated with the key or the `i`'th value doesn't exist, ## an exception is raised. {.cast(noSideEffect).}: return headers.table[headers.toCaseInsensitive(key)][i] proc `[]=`*(headers: HttpHeaders, key, value: string) = - ## Sets the header entries associated with ``key`` to the specified value. + ## Sets the header entries associated with `key` to the specified value. ## Replaces any existing values. headers.table[headers.toCaseInsensitive(key)] = @[value] proc `[]=`*(headers: HttpHeaders, key: string, value: seq[string]) = - ## Sets the header entries associated with ``key`` to the specified list of - ## values. Replaces any existing values. If ``value`` is empty, - ## deletes the header entries associated with ``key``. + ## Sets the header entries associated with `key` to the specified list of + ## values. Replaces any existing values. If `value` is empty, + ## deletes the header entries associated with `key`. if value.len > 0: headers.table[headers.toCaseInsensitive(key)] = value else: @@ -201,7 +200,7 @@ proc add*(headers: HttpHeaders, key, value: string) = headers.table[headers.toCaseInsensitive(key)].add(value) proc del*(headers: HttpHeaders, key: string) = - ## Deletes the header entries associated with ``key`` + ## Deletes the header entries associated with `key` headers.table.del(headers.toCaseInsensitive(key)) iterator pairs*(headers: HttpHeaders): tuple[key, value: string] = @@ -211,7 +210,7 @@ iterator pairs*(headers: HttpHeaders): tuple[key, value: string] = yield (k, value) func contains*(values: HttpHeaderValues, value: string): bool = - ## Determines if ``value`` is one of the values inside ``values``. Comparison + ## Determines if `value` is one of the values inside `values`. Comparison ## is performed without case sensitivity. for val in seq[string](values): if val.toLowerAscii == value.toLowerAscii: return true @@ -221,8 +220,8 @@ func hasKey*(headers: HttpHeaders, key: string): bool = func getOrDefault*(headers: HttpHeaders, key: string, default = @[""].HttpHeaderValues): HttpHeaderValues = - ## Returns the values associated with the given ``key``. If there are no - ## values associated with the key, then ``default`` is returned. + ## Returns the values associated with the given `key`. If there are no + ## values associated with the key, then `default` is returned. if headers.hasKey(key): return headers[key] else: @@ -236,15 +235,14 @@ func parseList(line: string, list: var seq[string], start: int): int = while start+i < line.len and line[start + i] notin {'\c', '\l'}: i += line.skipWhitespace(start + i) i += line.parseUntil(current, {'\c', '\l', ','}, start + i) - list.add(current) + list.add(move current) # implicit current.setLen(0) if start+i < line.len and line[start + i] == ',': i.inc # Skip , - current.setLen(0) func parseHeader*(line: string): tuple[key: string, value: seq[string]] = ## Parses a single raw header HTTP line into key value pairs. ## - ## Used by ``asynchttpserver`` and ``httpclient`` internally and should not + ## Used by `asynchttpserver` and `httpclient` internally and should not ## be used by you. result.value = @[] var i = 0 @@ -276,7 +274,7 @@ func contains*(methods: set[HttpMethod], x: string): bool = return parseEnum[HttpMethod](x) in methods func `$`*(code: HttpCode): string = - ## Converts the specified ``HttpCode`` into a HTTP status. + ## Converts the specified `HttpCode` into a HTTP status. runnableExamples: doAssert($Http404 == "404 Not Found") case code.int @@ -346,53 +344,25 @@ func `$`*(code: HttpCode): string = func `==`*(a, b: HttpCode): bool {.borrow.} -proc `==`*(rawCode: string, code: HttpCode): bool - {.deprecated: "Deprecated since v1.2; use rawCode == $code instead".} = - ## Compare the string form of the status code with a HttpCode - ## - ## **Note**: According to HTTP/1.1 specification, the reason phrase is - ## optional and should be ignored by the client, making this - ## proc only suitable for comparing the ``HttpCode`` against the - ## string form of itself. - return cmpIgnoreCase(rawCode, $code) == 0 - func is1xx*(code: HttpCode): bool {.inline, since: (1, 5).} = - ## Determines whether ``code`` is a 1xx HTTP status code. + ## Determines whether `code` is a 1xx HTTP status code. runnableExamples: doAssert is1xx(HttpCode(103)) - code.int in {100 .. 199} + code.int in 100 .. 199 func is2xx*(code: HttpCode): bool {.inline.} = - ## Determines whether ``code`` is a 2xx HTTP status code. - code.int in {200 .. 299} + ## Determines whether `code` is a 2xx HTTP status code. + code.int in 200 .. 299 func is3xx*(code: HttpCode): bool {.inline.} = - ## Determines whether ``code`` is a 3xx HTTP status code. - code.int in {300 .. 399} + ## Determines whether `code` is a 3xx HTTP status code. + code.int in 300 .. 399 func is4xx*(code: HttpCode): bool {.inline.} = - ## Determines whether ``code`` is a 4xx HTTP status code. - code.int in {400 .. 499} + ## Determines whether `code` is a 4xx HTTP status code. + code.int in 400 .. 499 func is5xx*(code: HttpCode): bool {.inline.} = - ## Determines whether ``code`` is a 5xx HTTP status code. - code.int in {500 .. 599} - -func `$`*(httpMethod: HttpMethod): string {.inline.} = - runnableExamples: - doAssert $HttpHead == "HEAD" - doAssert $HttpPatch == "PATCH" - doAssert $HttpGet == "GET" - doAssert $HttpPost == "POST" - - result = case httpMethod - of HttpHead: "HEAD" - of HttpGet: "GET" - of HttpPost: "POST" - of HttpPut: "PUT" - of HttpDelete: "DELETE" - of HttpTrace: "TRACE" - of HttpOptions: "OPTIONS" - of HttpConnect: "CONNECT" - of HttpPatch: "PATCH" + ## Determines whether `code` is a 5xx HTTP status code. + code.int in 500 .. 599 |