Comments (16)
Hmm it looks like we do set up the curl handle to upload on PATCH: see https://github.com/IBM-Swift/Kitura-net/blob/master/Sources/KituraNet/ClientRequest.swift#L419
Can you provide some example code showing the problem?
from kitura-net.
See code below
No issue when
httpMethod = "POST"
or
httpMethod = "PUT"
However when
httpMethod = "PATCH"
The body does not come through
I have to confess that being very new to all this my code is very made up from posts all around GitHub, StackOverflow and other blogs... I would not be fussed that you find something blatantly wrong in there.... :-S
Thanks
`
import SwiftyJSON
import KituraNet
func httpCall(httpUri: String, httpMethod: String, httpBody: String, queryDict: [String:String], httpHeaders: [String:String]) -> [String:Any] {
var returnDict = [String:Any]()
var queryString: String = ""
var i: Int = 0
for (key, value) in queryDict{
i+=1
queryString = queryString + key + "=" + value
if i != queryDict.count {
queryString = queryString + "&"
}
}
let httpQueryEncoded = queryString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
var httpRequestOptions:[ClientRequest.Options] = [
.schema(""),
.method(httpMethod),
.hostname(httpUri + "?" + httpQueryEncoded!),
.headers(httpHeaders)
]
var httpStatus: Int = 0
var httpResp: String = ""
let httpRequest = HTTP.request(httpRequestOptions) { response in
do {
httpResp = try response!.readString()!
print("httpResp: \(httpResp)")
httpStatus = try response!.status
if let jsonDictionary = JSON.parse(string: httpResp).dictionaryObject {
returnDict = jsonDictionary
print("jsonDictionary: \(jsonDictionary)")
} else {
returnDict = ["error": "Unknown data format returned"]
}
} catch {
httpResp = "error: http request returned an error"
//returnDict["seqHalt"] = "true"
}
}
httpRequest.write(from: httpBody)
httpRequest.end()
return returnDict
}`
from kitura-net.
Hi @laurentades :)
I've been looking into this issue for you, thanks for providing your example code!
I was wondering if you could also share the code where you call this function?
Basically I'm interested to know what parameters you're passing through to your httpCall(...) function :)
Thanks!
from kitura-net.
Hi @ddunn2
Sorry for long awaited reply.
The full code is below:
`/*
*/
import SwiftyJSON
import KituraNet
func httpCall(httpUri: String, httpMethod: String, httpBody: String, queryDict: [String:String], httpHeaders: [String:String]) -> [String:Any] {
var returnDict = [String:Any]()
var queryString: String = ""
var i: Int = 0
for (key, value) in queryDict{
i+=1
queryString = queryString + key + "=" + value
if i != queryDict.count {
queryString = queryString + "&"
}
}
print("httpUri: \(httpUri)")
print("queryString: \(queryString)")
print("httpHeaders: \(httpHeaders)")
print("httpBody: \(httpBody)")
let httpQueryEncoded = queryString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
var httpRequestOptions:[ClientRequest.Options] = [
.schema(""),
.method(httpMethod),
.hostname(httpUri + "?" + httpQueryEncoded!),
.headers(httpHeaders)
]
var httpStatus: Int = 0
var httpResp: String = ""
let httpRequest = HTTP.request(httpRequestOptions) { response in
do {
httpResp = try response!.readString()!
print("httpResp: \(httpResp)")
httpStatus = try response!.status
if let jsonDictionary = JSON.parse(string: httpResp).dictionaryObject {
returnDict = jsonDictionary // we got back JSON
print("jsonDictionary: \(jsonDictionary)")
} else {
returnDict = ["error": "Unknown data format returned"]
}
} catch {
httpResp = "error: http request returned an error"
//returnDict["seqHalt"] = "true"
}
}
httpRequest.write(from: httpBody)
httpRequest.end()
return returnDict
}
func main(args: [String:Any]) -> [String:Any] {
var returnDict = [String:Any]()
let seqHalt: String? = args["seqHalt"] as! String
let passThruData: [String:Any]? = args["passThruData"] as! [String:Any]
if seqHalt == "false" {
var httpResponse = [String:Any]()
let httpUri: String? = args["httpUri"] as! String // String
let httpMethod: String? = args["httpMethod"] as! String // String
let httpBody: String? = args["httpBody"] as! String // Dictionary
var httpHeaders: [String:String]? = args["httpHeaders"] as! [String:String] //Dictionary
var queryDict: [String:String]? = args["queryDict"] as! [String:String]
let pagingMode: String? = args["pagingMode"] as! String // String
let pageSize: String? = args["pageSize"] as! String // String
let jsonCombinePathString: String? = args["jsonCombinePathString"] as! String // String
let jsonCombinePath = jsonCombinePathString!.components(separatedBy: ",")
if pagingMode! == "none" {
returnDict = httpCall(httpUri: httpUri!, httpMethod: httpMethod!, httpBody: httpBody!, queryDict: queryDict!, httpHeaders: httpHeaders!)
} else {
var loopHalt: String = "false"
var loopCount: Int = 0
var dataArray = [[String:Any]]()
repeat {
loopCount += 1
switch pagingMode! {
case "recordRange":
queryDict!["fromIndex"] = String((loopCount - 1) * (Int(pageSize!)!) + 1)
queryDict!["toIndex"] = String(loopCount * Int(pageSize!)!)
httpResponse = httpCall(httpUri: httpUri!, httpMethod: httpMethod!, httpBody: httpBody!, queryDict: queryDict!, httpHeaders: queryDict!)
//print("httpResponse: \(httpResponse)")
var httpResponseJson = JSON(httpResponse)
let response = httpResponseJson["response"].dictionaryValue
let responseError = response["error"]
let responseNoData = response["nodata"]
let responseResult = response["result"]
if responseError != nil {
loopHalt = "true"
} else if responseNoData != nil {
loopHalt = "true"
} else {
dataArray = jsonCombine(baseArray: dataArray, addData: httpResponse, jsonCombinePathString: jsonCombinePathString!)
}
case "pageIndex":
print("pagingMode: \(pagingMode!)")
case "offset":
print("pagingMode: \(pagingMode!)")
default:
print("pagingMode: \(pagingMode!)")
}
} while loopHalt == "false"
}
}else{
returnDict["seqHalt"] = "true"
}
returnDict["passThruData"] = passThruData
return returnDict
}`
from kitura-net.
Hi,
Quick update.
Looks lie it is actually the target service acting up with the PATCH request rather than OpenWhisk struggling.
Will confirm further on.
from kitura-net.
Hi,
Eventually this is not something happening on the target API side: trying the patch method via Postman works just fine with the same parameters..
Any idea ?
from kitura-net.
Hi @laurentades
Thanks for providing the code and the updates!
I did some testing with this and also confirmed the patch method seems to be working when I setup my own project and passed through some custom parameters.
I'm going to attempt to recreate the problem you're running into using the code you've provided, once I have more information I'll update here as well.
from kitura-net.
@laurentades What parameters are you passing to your func main(args: [String:Any]) -> [String:Any] {...}
function?
from kitura-net.
Hi, the parameters passed to the main func is basically a json set of values which are extracted and passed to variables pretty much just after the beginning of the function:
httpUri: Base target API url
httpMethod: HTTP method (GET,POST,PUT,PATCH...)
httpBody: request body as a string
httpHeaders: Dictionary formatted set of values later-on processed into a request header
queryDict: Dictionary formatted set of values later-on processed into a request query string
pagingMode : text value used for specific API functionality (irrelevant here)
pageSize : numeric value used for specific API functionality (irrelevant here)
jsonCombinePathString : text value used for specific API functionality (irrelevant here)
Hope that helps.... :-)
Thanks !
from kitura-net.
Thanks for that! :)
Do you have the values you're passing in? Mainly I want to know the values you're passing into httpBody, httpHeaders and queryDict, this will allow me to recreate your exact issue :)
from kitura-net.
ah yes sure:
httpHeaders: ["Authorization": "Bearer keyDbQyg9ApRFyffG", "Content-type": "application/json; charset=utf-8", "Accept": "application/json"]
httpBody: {"fields":{"targetGP":0,"channelDiscount":0,"importUplift":0,"brandId":"1804391000001949436","brandName":"Generic Local"},"typecast":true}
In this case queryDict is empty
from kitura-net.
Thanks for providing that information!
So I've put together a little test scenario, and just want to confirm we're on the same page.
I created a simple Kitura server using the kitura init
command, added all necessary packages and then I edited the postInit()
function to look like this:
func postInit() throws {
// Capabilities
initializeMetrics(app: self)
// Endpoints
initializeHealthRoutes(app: self)
router.all(middleware: BodyParser())
router.patch("/") { request, response, next in
print(request.body)
response.send("OK")
next()
}
}
I then created a project that includes all the code you've provided and made the necessary call, the code looks like this:
import KituraNet
import SwiftyJSON
import Foundation
func httpCall(httpUri: String, httpMethod: String, httpBody: String, queryDict: [String:String], httpHeaders: [String:String]) -> [String:Any] {
var returnDict = [String:Any]()
var queryString: String = ""
var i: Int = 0
for (key, value) in queryDict{
i+=1
queryString = queryString + key + "=" + value
if i != queryDict.count {
queryString = queryString + "&"
}
}
print("httpUri: \(httpUri)")
print("queryString: \(queryString)")
print("httpHeaders: \(httpHeaders)")
print("httpBody: \(httpBody)")
let httpQueryEncoded = queryString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
var httpRequestOptions:[ClientRequest.Options] = [
.schema(""),
.method(httpMethod),
.hostname(httpUri + "?" + httpQueryEncoded!),
.headers(httpHeaders)
]
var httpStatus: Int = 0
var httpResp: String = ""
let httpRequest = HTTP.request(httpRequestOptions) { response in
do {
httpResp = try response!.readString()!
print("httpResp: \(httpResp)")
httpStatus = try response!.status
if let jsonDictionary = JSON.parse(string: httpResp).dictionaryObject {
returnDict = jsonDictionary // we got back JSON
print("jsonDictionary: \(jsonDictionary)")
} else {
returnDict = ["error": "Unknown data format returned"]
}
} catch {
httpResp = "error: http request returned an error"
//returnDict["seqHalt"] = "true"
}
}
httpRequest.write(from: httpBody)
httpRequest.end()
return returnDict
}
func main(args: [String:Any]) -> [String:Any] {
var returnDict = [String:Any]()
let seqHalt: String? = args["seqHalt"] as! String
let passThruData: [String:Any]? = args["passThruData"] as! [String:Any]
if seqHalt == "false" {
var httpResponse = [String:Any]()
let httpUri: String? = args["httpUri"] as! String // String
let httpMethod: String? = args["httpMethod"] as! String // String
let httpBody: String? = args["httpBody"] as! String // Dictionary
var httpHeaders: [String:String]? = args["httpHeaders"] as! [String:String] //Dictionary
var queryDict: [String:String]? = args["queryDict"] as! [String:String]
let pagingMode: String? = args["pagingMode"] as! String // String
let pageSize: String? = args["pageSize"] as! String // String
let jsonCombinePathString: String? = args["jsonCombinePathString"] as! String // String
let jsonCombinePath = jsonCombinePathString!.components(separatedBy: ",")
if pagingMode! == "none" {
returnDict = httpCall(httpUri: httpUri!, httpMethod: httpMethod!, httpBody: httpBody!, queryDict: queryDict!, httpHeaders: httpHeaders!)
} else {
var loopHalt: String = "false"
var loopCount: Int = 0
var dataArray = [[String:Any]]()
repeat {
loopCount += 1
switch pagingMode! {
case "recordRange":
queryDict!["fromIndex"] = String((loopCount - 1) * (Int(pageSize!)!) + 1)
queryDict!["toIndex"] = String(loopCount * Int(pageSize!)!)
httpResponse = httpCall(httpUri: httpUri!, httpMethod: httpMethod!, httpBody: httpBody!, queryDict: queryDict!, httpHeaders: queryDict!)
//print("httpResponse: \(httpResponse)")
var httpResponseJson = JSON(httpResponse)
let response = httpResponseJson["response"].dictionaryValue
let responseError = response["error"]
let responseNoData = response["nodata"]
let responseResult = response["result"]
if responseError != nil {
loopHalt = "true"
} else if responseNoData != nil {
loopHalt = "true"
} else {
// I commented this line out as I assume jsonCombine is a custom function you've created
// dataArray = jsonCombine(baseArray: dataArray, addData: httpResponse, jsonCombinePathString: jsonCombinePathString!)
}
case "pageIndex":
print("pagingMode: \(pagingMode!)")
case "offset":
print("pagingMode: \(pagingMode!)")
default:
print("pagingMode: \(pagingMode!)")
}
} while loopHalt == "false"
}
}else{
returnDict["seqHalt"] = "true"
}
returnDict["passThruData"] = passThruData
return returnDict
}
let result = main(args: ["httpUri": "http://localhost:8080",
"httpMethod": "PATCH",
"httpBody": "{\"fields\":{\"targetGP\":0,\"channelDiscount\":0,\"importUplift\":0,\"brandId\":\"1804391000001949436\",\"brandName\":\"Generic Local\"},\"typecast\":true}",
"queryDict": [:],
"httpHeaders": ["Authorization": "Bearer keyDbQyg9ApRFyffG", "Content-type": "application/json; charset=utf-8", "Accept": "application/json"],
"seqHalt": "false",
"pagingMode": "none",
"passThruData": ["": ""],
"pageSize": "",
"jsonCombinePathString": ""
])
print(result)
When I start up the server and run the code, I get this output on the server side:
Optional(Kitura.ParsedBody.json(["fields": {
brandId = 1804391000001949436;
brandName = "Generic Local";
channelDiscount = 0;
importUplift = 0;
targetGP = 0;
}, "typecast": 1]))
To me this suggests the body is being passed through? Is this the issue you're referring to? Or have I made a mistake in my recreation?
from kitura-net.
Hi,
What you are doing seems right. However that does not match what i am experiencing. As i wante to make sure the target API is no the issue, I use a generic request bin service to check data stream.
Here is that comes into this bin for the PUT request:
http://requestbin.fullcontact.com
PUT /11njq5e1 application/json; charset=utf-8
132 bytes 7s ago
From 158.175.125.86, 52.46.38.68
FORM/POST PARAMETERS
None
HEADERS
Cloudfront-Is-Tablet-Viewer: false
Connect-Time: 0
Host: requestbin.fullcontact.com
Total-Route-Time: 0
Content-Length: 132
Authorization: Bearer keyDbQyg9ApRFyffG
Cloudfront-Forwarded-Proto: http
X-Amz-Cf-Id: NYikN7fDMNoxRt9K6Ru7VU_mxWY5Eav1IdY1IuOIcoFCELbQxMq21Q==
Cloudfront-Viewer-Country: US
Cloudfront-Is-Smarttv-Viewer: false
Cloudfront-Is-Mobile-Viewer: false
Cloudfront-Is-Desktop-Viewer: true
X-Request-Id: d6ca222d-b816-41ba-b58c-2eb72fb9abbe
Via: 1.1 b8c7ca6d200a4b87d5b25c2d6f760752.cloudfront.net (CloudFront), 1.1 vegur
Accept: application/json
Connection: close
Content-Type: application/json; charset=utf-8
RAW BODY
{"fields":{"importUplift":0,"channelDiscount":0,"targetGP":-12,"brandName":"Adobe","brandId":"1804391000000697017"},"typecast":true}
and the PATCH request:
http://requestbin.fullcontact.com
PATCH /11njq5e1 application/json; charset=utf-8
0 bytes 1m ago
From 158.175.125.86, 52.46.38.43
FORM/POST PARAMETERS
None
HEADERS
Cloudfront-Is-Tablet-Viewer: false
Connect-Time: 0
Host: requestbin.fullcontact.com
Total-Route-Time: 0
Authorization: Bearer keyDbQyg9ApRFyffG
Cloudfront-Forwarded-Proto: http
Content-Type: application/json; charset=utf-8
X-Amz-Cf-Id: XrxWO0jz5l_bAYzbsqz7rI4eCctYxmxMYBZ2vqReC7tvrah46XNo_Q==
Cloudfront-Is-Smarttv-Viewer: false
Cloudfront-Is-Mobile-Viewer: false
Cloudfront-Is-Desktop-Viewer: true
Content-Length: 0
Via: 1.1 fff10bd55076b11530aa8b26ee2c0b65.cloudfront.net (CloudFront), 1.1 vegur
Accept: application/json
X-Request-Id: 698511af-8546-4593-98dd-419893da5813
Cloudfront-Viewer-Country: US
Connection: close
RAW BODY
None
That is consistent with what i am experiencing in my test environment to my target API.
Does that help ?
from kitura-net.
Hi,
Apologies for the delay. Thanks for providing that information it's very helpful!
I'll review what you've provided and see if I can find the cause of the issue.
from kitura-net.
Hey!
Can you provide the code for your router.patch(...)
method on your server?
Thanks :)
from kitura-net.
errr... no idea what this is :-)
I am just using KituraNet as a 'Import' in IBM Bluemix cloud functions. What I have shared so far is basically all i have written for this function...
Hope that's not a show-stopper...!
Thanks
from kitura-net.
Related Issues (20)
- Implement test for ServerLifecycleListener.performClientConnectionFailCallbacks
- EXC_BAD_ACCESS when reading HTTPServerRequest.method with SwiftMetricsKitura HOT 4
- EXC_BAD_INSTRUCTION on IncomingSocketHandler.swift line 197 (only Mac OS X) HOT 7
- missing required modules: 'CCurl', 'CHTTPParser' HOT 1
- Undeprecate `urlComponents`?
- Thread sanitizer finds issues HOT 7
- Errors in removeIdleSockets HOT 8
- Valid port numbers not representable HOT 6
- Use ephemeral ports in KituraNet tests HOT 1
- Port Kitura Request/Response tests to Kitura-net HOT 1
- Kitura-net ignores an Expect: 100-continue header HOT 1
- Validate curl 7.59 fix for chunked encoding HOT 2
- HTTPStatusCode enum is missing registered values HOT 1
- Gracefully close connections on server shutdown
- Study: Does the HTTP smuggling attack affect Kitura-net?
- ClientRequest error. on Linux HOT 2
- Error=Parsed fewer bytes than were passed to the HTTP parser HOT 5
- How make unix domain sock http req
- Update for Swift 5.2 needed HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from kitura-net.