12
12
//
13
13
//===----------------------------------------------------------------------===//
14
14
15
- import class Foundation. JSONEncoder
16
15
import Logging
17
16
import NIO
18
17
import NIOHTTP1
@@ -68,14 +67,10 @@ extension Lambda {
68
67
body = buffer
69
68
case . failure( let error) :
70
69
url += Consts . postErrorURLSuffix
71
- let error = ErrorResponse ( errorType: Consts . functionError, errorMessage: " \( error) " )
72
- switch error. toJson ( ) {
73
- case . failure( let jsonError) :
74
- return self . eventLoop. makeFailedFuture ( RuntimeError . json ( jsonError) )
75
- case . success( let json) :
76
- body = self . allocator. buffer ( capacity: json. utf8. count)
77
- body!. writeString ( json)
78
- }
70
+ let errorResponse = ErrorResponse ( errorType: Consts . functionError, errorMessage: " \( error) " )
71
+ let bytes = errorResponse. toJSONBytes ( )
72
+ body = self . allocator. buffer ( capacity: bytes. count)
73
+ body!. writeBytes ( bytes)
79
74
}
80
75
logger. debug ( " reporting results to lambda runtime engine using \( url) " )
81
76
return self . httpClient. post ( url: url, body: body) . flatMapThrowing { response in
@@ -99,28 +94,23 @@ extension Lambda {
99
94
func reportInitializationError( logger: Logger , error: Error ) -> EventLoopFuture < Void > {
100
95
let url = Consts . postInitErrorURL
101
96
let errorResponse = ErrorResponse ( errorType: Consts . initializationError, errorMessage: " \( error) " )
102
- var body : ByteBuffer
103
- switch errorResponse. toJson ( ) {
104
- case . failure( let jsonError) :
105
- return self . eventLoop. makeFailedFuture ( RuntimeError . json ( jsonError) )
106
- case . success( let json) :
107
- body = self . allocator. buffer ( capacity: json. utf8. count)
108
- body. writeString ( json)
109
- logger. warning ( " reporting initialization error to lambda runtime engine using \( url) " )
110
- return self . httpClient. post ( url: url, body: body) . flatMapThrowing { response in
111
- guard response. status == . accepted else {
112
- throw RuntimeError . badStatusCode ( response. status)
113
- }
114
- return ( )
115
- } . flatMapErrorThrowing { error in
116
- switch error {
117
- case HTTPClient . Errors. timeout:
118
- throw RuntimeError . upstreamError ( " timeout " )
119
- case HTTPClient . Errors. connectionResetByPeer:
120
- throw RuntimeError . upstreamError ( " connectionResetByPeer " )
121
- default :
122
- throw error
123
- }
97
+ let bytes = errorResponse. toJSONBytes ( )
98
+ var body = self . allocator. buffer ( capacity: bytes. count)
99
+ body. writeBytes ( bytes)
100
+ logger. warning ( " reporting initialization error to lambda runtime engine using \( url) " )
101
+ return self . httpClient. post ( url: url, body: body) . flatMapThrowing { response in
102
+ guard response. status == . accepted else {
103
+ throw RuntimeError . badStatusCode ( response. status)
104
+ }
105
+ return ( )
106
+ } . flatMapErrorThrowing { error in
107
+ switch error {
108
+ case HTTPClient . Errors. timeout:
109
+ throw RuntimeError . upstreamError ( " timeout " )
110
+ case HTTPClient . Errors. connectionResetByPeer:
111
+ throw RuntimeError . upstreamError ( " connectionResetByPeer " )
112
+ default :
113
+ throw error
124
114
}
125
115
}
126
116
}
@@ -142,15 +132,48 @@ internal struct ErrorResponse: Codable {
142
132
var errorMessage : String
143
133
}
144
134
145
- private extension ErrorResponse {
146
- func toJson( ) -> Result < String , Error > {
147
- let encoder = JSONEncoder ( )
148
- do {
149
- let data = try encoder. encode ( self )
150
- return . success( String ( data: data, encoding: . utf8) ?? " unknown error " )
151
- } catch {
152
- return . failure( error)
135
+ internal extension ErrorResponse {
136
+ func toJSONBytes( ) -> [ UInt8 ] {
137
+ var bytes = [ UInt8] ( )
138
+ bytes. append ( UInt8 ( ascii: " { " ) )
139
+ bytes. append ( contentsOf: #""errorType":"# . utf8)
140
+ self . encodeString ( self . errorType, bytes: & bytes)
141
+ bytes. append ( contentsOf: #","errorMessage":"# . utf8)
142
+ self . encodeString ( self . errorMessage, bytes: & bytes)
143
+ bytes. append ( UInt8 ( ascii: " } " ) )
144
+ return bytes
145
+ }
146
+
147
+ private func encodeString( _ string: String , bytes: inout [ UInt8 ] ) {
148
+ bytes. append ( UInt8 ( ascii: " \" " ) )
149
+ let stringBytes = string. utf8
150
+ var startCopyIndex = stringBytes. startIndex
151
+ var nextIndex = startCopyIndex
152
+
153
+ while nextIndex != stringBytes. endIndex {
154
+ switch stringBytes [ nextIndex] {
155
+ case 0 ..< 32 , UInt8 ( ascii: " \" " ) , UInt8 ( ascii: " \\ " ) :
156
+ // All Unicode characters may be placed within the
157
+ // quotation marks, except for the characters that MUST be escaped:
158
+ // quotation mark, reverse solidus, and the control characters (U+0000
159
+ // through U+001F).
160
+ // https://tools.ietf.org/html/rfc7159#section-7
161
+
162
+ // copy the current range over
163
+ bytes. append ( contentsOf: stringBytes [ startCopyIndex ..< nextIndex] )
164
+ bytes. append ( UInt8 ( ascii: " \\ " ) )
165
+ bytes. append ( stringBytes [ nextIndex] )
166
+
167
+ nextIndex = stringBytes. index ( after: nextIndex)
168
+ startCopyIndex = nextIndex
169
+ default :
170
+ nextIndex = stringBytes. index ( after: nextIndex)
171
+ }
153
172
}
173
+
174
+ // copy everything, that hasn't been copied yet
175
+ bytes. append ( contentsOf: stringBytes [ startCopyIndex ..< nextIndex] )
176
+ bytes. append ( UInt8 ( ascii: " \" " ) )
154
177
}
155
178
}
156
179
0 commit comments