How to get value of SIP header in Freeswitch? - sip

I need to get value of CALLED_DID header and do some actions in dialplan, but i don't know how.
I've tried to use ${sip_h_CALLED_DID} but it's empty, because have no X- prefix before header name.
Is there any other method to extract value from SIP header?
Please help me, i've read allover the internet but can't find answer.
INVITE sip:gw+zadarma-rbcrm#185.70.135.33:65000;transport=udp;gw=zadarma-rbcrm SIP/2.0
Record-Route: <sip:185.45.152.161;lr=on;ftag=as6a38207b>
Via: SIP/2.0/UDP 185.45.152.161;branch=z9hG4bK26d.6cf33cf5d2cdd6683e8de9503870f397.0
Via: SIP/2.0/UDP 185.45.152.148:5060;rport=5060;branch=z9hG4bK74d97ef6
Max-Forwards: 69
From: "+79630495339" <sip:+79630495339#sip.zadarma.com>;tag=as6a38207b
To: <sip:346127#185.45.152.161>
Contact: <sip:+79630495339#185.45.152.148:5060>
Call-ID: 401671d34e8247a9694c3da87c97fbbb#185.45.152.148:5060
CSeq: 102 INVITE
User-Agent: Zadarma Voip
Date: Thu, 07 Mar 2019 07:38:22 GMT
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces
CALLED_DID: 73433023519
Content-Type: application/sdp
Content-Length: 415
hostname: sipbalancer-1
cc_num: 346127
cc_counter: 1

It is possible by setting
<param name="parse-all-invite-headers" value="true"/> in the Sofia SIP Profile. Then all headers from the Invite are set as sip_i_Header-Name channel variables.
Reacting to specific Headears in any other SIP-Messages:
If you want to react to specific headers on other messages, you can do this via setting the variable sip_watch_headers (needs to be exported and prefixed with nolocal if you want it for B-leg only)
If the header is detected, you will get a CUSTOM event of the Subclass "sofia::notify_watched_header".
example to detect Reason header on B-Leg:
<action application="export" data="_nolocal_sip_watch_headers=Reason"/>
Here is an example of the Event looking for a Reason header on the B-Leg, watching the Header "Reason":
"Event-Name": "CUSTOM",
...
"Event-Calling-File": "sofia.c",
"Event-Calling-Function": "notify_watched_header",
"Event-Calling-Line-Number": "1443",
"Event-Sequence": "98672",
"Event-Subclass": "sofia::notify_watched_header",
"SIP-Message": "SIP/2.0 183 Session Progress",
"Header-Name": "Reason",
"Header-Value": "Q.850;cause=16",
"Channel-State": "CS_CONSUME_MEDIA",
"Channel-Call-State": "DOWN",
"Channel-State-Number": "7",
...
"Call-Direction": "outbound",
This event can be reacted either with Lua by setting a hook script in the Lua configuration or via AMQP / EventSocket.
How to do react to these events with Lua
https://freeswitch.org/confluence/display/FREESWITCH/mod_lua#Event_Hooks
Example: autload_configs/lua.conf.xml:
<configuration name="lua.conf" description="LUA Configuration">
<settings>
<param name="module-directory" value="/etc/freeswitch/scripts/?.so"/>
<param name="script-directory" value="/etc/freeswitch/scripts/?.lua"/>
<!--<param name="startup-script" value="startup_script_1.lua"/>--> <!-- started at fs startup and maybe lives forever -->
<hook event="CHANNEL_DESTROY" script="/etc/freeswitch/scripts/on_channel_destroy.lua"/>
<hook event="CUSTOM" subclass="sofia::notify_watched_header" script="/etc/freeswitch/scripts/on_reason_header.lua"/>
</settings>
</configuration>
Example Lua script
local uuid = event:getHeader("Unique-ID")
local shallHangup = event:getHeader("variable_HangupOnReasonInEarly")
local answerState = event:getHeader("Answer-State")
if (shallHangup ~= nil and shallHangup == "true" and answerState == "ringing") then
local value = event:getHeader("Header-Value")
local code = value:match(";cause=(%d*)")
--local data = event:serialize("json")
freeswitch.consoleLog("INFO","REASON DETECTED: for: " .. uuid .. "\n")
api = freeswitch.API()
api:executeString("uuid_kill " .. uuid .. " " .. code)
end
To enable it in dialplan:
<action application="export" data="_nolocal_sip_watch_headers=Reason"/>
<action application="export" data="_nolocal_HangupOnReasonInEarly=true"/>
<action application="bridge" data="..."/>

Sip values can not be easily extracted.
Prefix the header with X-. Anything else is invalid.
You can recompile mod_sofia - add an extra header reader:
https://freeswitch.org/stash/projects/FS/repos/freeswitch/browse/src/mod/endpoints/mod_sofia/sofia.c
Example line 11297.
Add this:
} else if (!strcasecmp(un->un_name, "CALLED_DID")) {
switch_channel_set_variable(channel, "called_did", un->un_value);
Between:
} else if (!strcasecmp(un->un_name, "Geolocation")) {
switch_channel_set_variable(channel, "sip_geolocation", un->un_value);
And:
} else if (!strcasecmp(un->un_name, "Geolocation-Error")) {`
switch_channel_set_variable(channel, "sip_user_location", un->un_value);
And retrieve this header in dialplan like this:
<action application="log" data="DEBUG Called Did -> ${called_did}"/>

After reading a ton of material, I came to the conclusion:
Reading of custom header without X- prefix is not possible without modifying of source code.
But this is not acceptable way in my case.

Related

Perl: Connecting to Walmart API to load items Unsupported Media type error

We are trying using the marketplace.walmartapis.com api to bulk list items and it is returning unsupported media type for xml item feed
We are using Perl and are able to perform other commands to update inventory and pricing, check orders, lookup feeds and skus etc. The token SHOULD be good, as we ran two calls back to back using the same token and the first call (item inventory) worked as expected before trying to send the xml file. It is only when we try to send the xml batch file that we run into the problem.
We assume the issue is with the header/authentication and have tried many different combinations. But obviously we are missing something.
What is being sent:
POST https://marketplace.walmartapis.com/v3/feeds?feedType=item HTTP/1.1
Headers:
Authorization: Basic MDc4......
Content-Length: 2277
Accept: application/xml
Content-Type: multipart/formdata
Host: marketplace.walmartapis.com
WM_QOS.CORRELATION_ID: TB123456V32
WM_SEC.ACCESS_TOKEN: eyJra...
WM_SVC.NAME: Walmart Marketplace
Boundary: 1234ran4321dom5678boundary
formdata payload:
--1234ran4321dom5678boundary
Content-Disposition: formdata; name="xml"
Content-length: 2151
<?xml version="1.0" encoding="UTF-8"?>
<MPItemFeed xmlns="http://walmart.com/">
<MPItemFeedHeader>
<version>3.1</version>
<requestBatchId>......</requestBatchId>
<feedDate>2019-02-18T19:45:17</feedDate>
<mart>WALMART_US</mart>
</MPItemFeedHeader>
<MPItem>
<processMode>CREATE</processMode>
<sku>....</sku>
<productIdentifiers>
<productIdentifier>
<productIdType>UPC</productIdType>
<productId>..........</productId>
</productIdentifier>
</productIdentifiers>
<MPProduct>
<productName>.....................</productName>
<category>
<SportAndRecreation>
<SportAndRecreationOther>
<shortDescription>........................</shortDescription>
<keyFeatures>
<keyFeaturesValue>I...............</keyFeaturesValue>
</keyFeatures>
<brand>............</brand>
<manufacturer>.............</manufacturer>
<manufacturerPartNumber>.............</manufacturerPartNumber>
<modelNumber>..</modelNumber>
<mainImageUrl>............</mainImageUrl>
<count>1</count>
<isProp65WarningRequired>No</isProp65WarningRequired>
<sportsLeague>
<sportsLeagueValue>....</sportsLeagueValue>
</sportsLeague>
<keywords>................</keywords>
<isMemorabilia>......</isMemorabilia>
<isCollectible>...........</isCollectible>
</SportAndRecreationOther>
</SportAndRecreation>
</category>
</MPProduct>
</MPOffer>
<price>21.95</price>
<StartDate>2019-02-23T19:45:17</StartDate>
<EndDate>2019-04-19T19:45:17</EndDate>
<ShippingWeight>
<measure>2</measure>
<unit>lb</unit>
</ShippingWeight>
<ProductTaxCode>2038345</ProductTaxCode>
</MPOffer>
</MPItem>
</MPItemFeed>
--1234ran4321dom5678boundary--
back from Walmart.pm
$HASH = {
"error" => {
"category" => "DATA",
"causes" => {},
"code" => "UNSUPPORTED_MEDIA_TYPE.GMP_GATEWAY_API",
"errorIdentifiers" => {},
"info" => "Unsupported Media Type.",
"severity" => "ERROR"
},
"xmlns:ns2" => "http://walmart.com/"
};
Any pointers in the right direction would be appreciated.
Thanks for everyone that looked. The problem seems to be solved
It was one minor typo
multipart/formdata needed to be multipart/form-data
With that change now we are getting a feed id that shows up in the Seller area

Using SwiftNIO and SwiftNIOHTTP2 as an HTTP2 client

I'm currently working on a simple HTTP2 client in Swift using SwiftNIO and the SwiftNIOHTTP2 beta.
My implementation looks like this:
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let bootstrap = ClientBootstrap(group: group)
.channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.channelInitializer { channel in
channel.pipeline.add(handler: HTTP2Parser(mode: .client)).then {
let multiplexer = HTTP2StreamMultiplexer { (channel, streamID) -> EventLoopFuture<Void> in
return channel.pipeline.add(handler: HTTP2ToHTTP1ClientCodec(streamID: streamID, httpProtocol: .https))
}
return channel.pipeline.add(handler: multiplexer)
}
}
defer {
try! group.syncShutdownGracefully()
}
let url = URL(string: "https://strnmn.me")!
_ = try bootstrap.connect(host: url.host!, port: url.port ?? 443)
.wait()
Unfortunately the connection always fails with an error:
nghttp2 error: Remote peer returned unexpected data while we expected SETTINGS frame. Perhaps, peer does not support HTTP/2 properly.
However, connecting and issuing a simple request using nghttp2 from the command line works fine.
$ nghttp -vn https://strnmn.me
[ 0.048] Connected
The negotiated protocol: h2
[ 0.110] recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
(niv=3)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):128]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]
[SETTINGS_MAX_FRAME_SIZE(0x05):16777215]
[ 0.110] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
(window_size_increment=2147418112)
[ 0.110] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[ 0.110] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[ 0.110] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
(dep_stream_id=0, weight=201, exclusive=0)
[ 0.110] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
(dep_stream_id=0, weight=101, exclusive=0)
[ 0.110] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
(dep_stream_id=0, weight=1, exclusive=0)
[ 0.110] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
(dep_stream_id=7, weight=1, exclusive=0)
[ 0.110] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
(dep_stream_id=3, weight=1, exclusive=0)
[ 0.111] send HEADERS frame <length=35, flags=0x25, stream_id=13>
; END_STREAM | END_HEADERS | PRIORITY
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
; Open new stream
:method: GET
:path: /
:scheme: https
:authority: strnmn.me
accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/1.34.0
[ 0.141] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[ 0.141] recv (stream_id=13) :status: 200
[ 0.141] recv (stream_id=13) server: nginx
[ 0.141] recv (stream_id=13) date: Sat, 24 Nov 2018 16:29:13 GMT
[ 0.141] recv (stream_id=13) content-type: text/html
[ 0.141] recv (stream_id=13) last-modified: Sat, 01 Jul 2017 20:23:11 GMT
[ 0.141] recv (stream_id=13) vary: Accept-Encoding
[ 0.141] recv (stream_id=13) etag: W/"595804af-8a"
[ 0.141] recv (stream_id=13) expires: Sat, 24 Nov 2018 16:39:13 GMT
[ 0.141] recv (stream_id=13) cache-control: max-age=600
[ 0.141] recv (stream_id=13) x-frame-options: SAMEORIGIN
[ 0.141] recv (stream_id=13) content-encoding: gzip
[ 0.141] recv HEADERS frame <length=185, flags=0x04, stream_id=13>
; END_HEADERS
(padlen=0)
; First response header
[ 0.142] recv DATA frame <length=114, flags=0x01, stream_id=13>
; END_STREAM
[ 0.142] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
(last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])
How can I establish a session and issue a GET request using SwiftNIOHTTP2?
That's a very good question! Let's first analyse why this is more complicated than sending a HTTP/1.x request. Broadly speaking these issues fall into two categories:
NIO at the moment makes it more complicated than necessary, therefore much of what I'll write further down can be unintuitive at times. I'm one of the NIO core team and even I had to dig through quite a bit of code to get this fully working, mostly because we still don't have doc generation for swift-nio-ssl and swift-nio-http2 on http://docs.swiftnio.io .
HTTP/2 is just much more complicated than HTTP/1 and NIO is more a toolbox that can be used to build HTTP clients so we need to use a bunch of tools together to get it all working.
I'll focus on the necessary complexity (2) here and will file bugs/fixes for (1). Let's check what tools we need from the NIO toolbox to get this working:
TLS. No real-world HTTP/2 server will allow you to speak HTTP/2 over plaintext
ALPN. HTTP/1 and HTTP/2 share the same port (usually 443) so we need to tell the server that we want to speak HTTP/2 because for backwards compatibility the default remains HTTP/1. We can do this using a mechanism called ALPN (Application-layer Protocol Negotiation), the other option would be to perform a HTTP/1 upgrade to HTTP2 but that's both more complicated and less performant so let's not do this here
some HTTP/2 tools: a) open a new HTTP/2 b) HTTP/2 to HTTP/1 message translation c) HTTP/2 multiplexing
The code in your question contains the most important bits, namely 3b and 3c of the above list. But we need to add 1, 2 and 3a so let's do this :)
Let's start with 2) ALPN:
let tlsConfig = TLSConfiguration.forClient(applicationProtocols: ["h2"])
let sslContext = try SSLContext(configuration: tlsConfig)
This is an SSL configuration with the "h2" ALPN protocol identifier there which will tell the server that we want to speak HTTP/2 as documented in the HTTP/2 spec.
Ok, let's add TLS with the sslContext set up before:
let sslHandler = try! OpenSSLClientHandler(context: sslContext, serverHostname: hostname)
It's also important that we tell the OpenSSLClientHandler the server's hostname so it can validate the certificate properly.
Lastly we need to do 3a (creating a new HTTP/2 stream to issue our request on) which can be easily done using a ChannelHandler:
/// Creates a new HTTP/2 stream when our channel is active and adds the `SendAGETRequestHandler` so a request is sent.
final class CreateRequestStreamHandler: ChannelInboundHandler {
typealias InboundIn = Never
private let multiplexer: HTTP2StreamMultiplexer
private let responseReceivedPromise: EventLoopPromise<[HTTPClientResponsePart]>
init(multiplexer: HTTP2StreamMultiplexer, responseReceivedPromise: EventLoopPromise<[HTTPClientResponsePart]>) {
self.multiplexer = multiplexer
self.responseReceivedPromise = responseReceivedPromise
}
func channelActive(ctx: ChannelHandlerContext) {
func requestStreamInitializer(channel: Channel, streamID: HTTP2StreamID) -> EventLoopFuture<Void> {
return channel.pipeline.addHandlers([HTTP2ToHTTP1ClientCodec(streamID: streamID, httpProtocol: .https),
SendAGETRequestHandler(responseReceivedPromise: self.responseReceivedPromise)],
first: false)
}
// this is the most important line: When the channel is active we add the `HTTP2ToHTTP1ClientCodec` to deal in HTTP/1 messages as well as the `SendAGETRequestHandler` which will send a request.
self.multiplexer.createStreamChannel(promise: nil, requestStreamInitializer)
}
}
Okay, that's the scaffolding done. The SendAGETRequestHandler is the last part which is a handler that will be added as soon as the new HTTP/2 stream that we have opened before has been opened successfully. To see the full response, I also implemented accumulating all bits of the response together into a promise:
/// Fires off a GET request when our stream is active and collects all response parts into a promise.
///
/// - warning: This will read the whole response into memory and delivers it into a promise.
final class SendAGETRequestHandler: ChannelInboundHandler {
typealias InboundIn = HTTPClientResponsePart
typealias OutboundOut = HTTPClientRequestPart
private let responseReceivedPromise: EventLoopPromise<[HTTPClientResponsePart]>
private var responsePartAccumulator: [HTTPClientResponsePart] = []
init(responseReceivedPromise: EventLoopPromise<[HTTPClientResponsePart]>) {
self.responseReceivedPromise = responseReceivedPromise
}
func channelActive(ctx: ChannelHandlerContext) {
assert(ctx.channel.parent!.isActive)
var reqHead = HTTPRequestHead(version: .init(major: 2, minor: 0), method: .GET, uri: "/")
reqHead.headers.add(name: "Host", value: hostname)
ctx.write(self.wrapOutboundOut(.head(reqHead)), promise: nil)
ctx.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil)
}
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
let resPart = self.unwrapInboundIn(data)
self.responsePartAccumulator.append(resPart)
if case .end = resPart {
self.responseReceivedPromise.succeed(result: self.responsePartAccumulator)
}
}
}
To finish it up, let's set up the client's channel pipeline:
let bootstrap = ClientBootstrap(group: group)
.channelInitializer { channel in
let myEventLoop = channel.eventLoop
let sslHandler = try! OpenSSLClientHandler(context: sslContext, serverHostname: hostname)
let http2Parser = HTTP2Parser(mode: .client)
let http2Multiplexer = HTTP2StreamMultiplexer { (channel, streamID) -> EventLoopFuture<Void> in
return myEventLoop.newSucceededFuture(result: ())
}
return channel.pipeline.addHandlers([sslHandler,
http2Parser,
http2Multiplexer,
CreateRequestStreamHandler(multiplexer: http2Multiplexer,
responseReceivedPromise: responseReceivedPromise),
CollectErrorsAndCloseStreamHandler(responseReceivedPromise: responseReceivedPromise)],
first: false).map {
}
}
To see a fully working example, I put something together a PR for swift-nio-examples/http2-client.
Oh, and the reason that NIO was claiming that the other end isn't speaking HTTP/2 properly was the lack of TLS. There was no OpenSSLHandler so NIO was speaking plaintext HTTP/2 to a remote end which was speaking TLS and then the two peers don't understand each other :).

Attachment missing in MTOM response from Citrus SOAP server simulation

I have built a sample Citrus testcase to simulate a SOAP server that responds with an MTOM attachment.
runner.soap(action -> action.server("simulationServer")
.receive()
...[validation etc]
);
runner.soap(action -> action.server("simulationServer")
.send()
.name("get-response")
.mtomEnabled(Boolean.TRUE)
.attachment("myAttachment", "application/octet-stream", new ClassPathResource("testfiles/myAttachment.pdf"))
.payload("<getResponse xmlns:xmime=\"http://www.w3.org/2005/05/xmlmime\">\n" +
" <document>\n" +
" <contentElements>\n" +
" <contentElement xmime:contentType=\"application/pdf\">cid:myAttachment</contentElement>\n" +
" </contentElements>\n" +
" <id>Test</id>\n" +
" </document>\n" +
"</getResponse>\n")
);
When I run this test and call the Citrus simulation with SoapUI, I see the contents of myAttachment.pdf in the debug logs. So at least it looks like Citrus tries to send the attachment.
However, in SoapUI I do not get an attachment. There is a XOP element in the SOAP response, but no attachment. The RAW view of SoapUI of the Citrus response looks like this.
HTTP/1.1 200 OK
Date: Tue, 16 Jan 2018 15:30:36 GMT
Accept: text/xml, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
SOAPAction: ""
Content-Type: Multipart/Related; boundary="----=_Part_0_382348859.1516116636524"; type="application/xop+xml"; start-info="text/xml"
Transfer-Encoding: chunked
Server: Jetty(9.4.6.v20170531)
------=_Part_0_382348859.1516116636524
Content-Type: application/xop+xml; charset=utf-8; type="text/xml"
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><getResponse xmlns:xmime="http://www.w3.org/2005/05/xmlmime">
<document>
<contentElements>
<contentElement xmime:contentType="application/pdf"><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:myAttachment"/></contentElement>
</contentElements>
<id>Test</id>
</document>
</getResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
------=_Part_0_382348859.1516116636524--
In an MTOM response with attachment the attachment starts where this RAW view ends. It should continue like this
------=_Part_0_382348859.1516116636524-- [last line from above]
Content-Type: application/pdf
Content-Transfer-Encoding: binary
Content-ID: <myAttachment>
%PDF-1.4... [PDF content]
I am using Citrus 2.7.2 release.
Update
Still no success on this. Wireshark shows the same picture as SoapUI: the attachment is missing in the response.
However, when I debug into the code on the server (Citrus) side, I see the attachment in the response message until I get lost somewhere in a MessageSendingTemplate. Same on the console log. The message has the attachment.
There is an inline MTOM variant in the Citrus documentation, but I can't find a way to set this mtom-inline on the attachment in Java config.
Any hints, where to set a breakpoint to find where the attachment get lost? Or any other suggestions/examples on the Citrus side?
The setMtomInline field sits on the SoapAttachment interface. I am not sure if I got the setup right - but seems to work for inlined attachements - fails for soap attachements / multipart. The SoapUI Mock does not show any attachements when receiving requests from following testcase.
SoapAttachment soapAttachment = new SoapAttachment();
soapAttachment.setMtomInline(false);
soapAttachment.setContentResourcePath("log4j.xml");
soapAttachment.setContentType("application/octet-stream");
soapAttachment.setContentId("FILE");
SoapMessage soapMessage = new SoapMessage();
soapMessage.mtomEnabled(true);
soapMessage.soapAction("/HelloService/sayHello");
soapMessage.setPayload(
"<ht:HelloRequest " +
"xmlns:ht=\"http://citrusframework.org/schemas/samples/HelloMtomService\" " +
"xmlns:xop=\"http://www.w3.org/2004/08/xop/include\" >\n" +
" <ht:Message>Hei .. citrus does stream mtom</ht:Message>\n" +
" <ht:Data><xop:Include href=\"cid:FILE\"/></ht:Data>\n" +
"</ht:HelloRequest>");
soapMessage.addAttachment(soapAttachment);
runner.soap(action -> {
action.client("helloMtomSoapuiClient")
.send()
.soapAction("/HelloService/sayHello")
.message(soapMessage);
});
If I do the same for MtomInline set to true, I see the attachement as base64 encoded content text in the ht:Data node.
SoapAttachment soapAttachment = new SoapAttachment();
soapAttachment.setContentResourcePath("log4j.xml");
soapAttachment.setMtomInline(true);
soapAttachment.setContentType("application/xml");
soapAttachment.setContentId("MyAttachement");
soapAttachment.setEncodingType("base64Binary");
runner.soap(action -> {
action.client("helloMtomSoapuiClient")
.send()
.soapAction("/HelloService/sayHello")
.mtomEnabled(true)
.payload("<ht:HelloRequest xmlns:ht=\"http://citrusframework.org/schemas/samples/HelloMtomService\">\n" +
" <ht:Message>Hei .. citrus does mtom</ht:Message>\n" +
" <ht:Data>cid:MyAttachement</ht:Data>\n" +
"</ht:HelloRequest>")
.attachment(soapAttachment);
});
Either soapUI or citrus swallows the attachement. Some help or working JavaDSL sample would be nice.
It was actually a bug that will be fixed in Citrus 2.7.4 release. See https://github.com/christophd/citrus/issues/328
The inline MTOM variant with XML config works for me in the current release.
<ws:send endpoint="simulationServer" mtom-enabled="true">
<message>
<resource file="testfiles/simulation/get-response.xml" />
</message>
<ws:attachment content-id="myAttachment" content-type="application/octet-stream" mtom-inline="true" encoding-type="base64Binary">
<ws:resource file="classpath:testfiles/myAttachment.pdf"/>
</ws:attachment>
</ws:send>

Azure media service job creation fails using rest api

Trying to consume Azure media service rest api. (following the tutorial : https://learn.microsoft.com/en-us/azure/media-services/media-services-rest-get-started)
Everything works fine until the point I try to create a Job. Sending the same request as in example (except asset id and token) and getting response :
Parsing request content failed due to: Make sure to only use property names that are defined by the type
Request:
POST https://wamsdubclus001rest-hs.cloudapp.net/api/Jobs HTTP/1.1
Connection: Keep-Alive
Content-Type: application/json
Accept: application/json; odata=verbose
Accept-Charset: UTF-8
Authorization: Bearer token -> here i send real token
DataServiceVersion: 1.0;NetFx
MaxDataServiceVersion: 3.0;NetFx
x-ms-version: 2.11
Content-Length: 458
Host: wamsdubclus001rest-hs.cloudapp.net
{
"Name":"TestJob",
"InputMediaAssets":[
{
"__metadata":{
"uri":"https://wamsdubclus001rest-hs.cloudapp.net/api/Assets('nb%3Acid%3AUUID%3A5168b52a-68ed-4df1-bac8-0648ce734ff6')"
}
}
],
"Tasks":[
{
"Configuration":"Adaptive Streaming",
"MediaProcessorId":"nb:mpid:UUID:ff4df607-d419-42f0-bc17-a481b1331e56",
"TaskBody":"<?xml version=\"1.0\" encoding=\"utf-8\"?><taskBody><inputAsset>JobInputAsset(0)</inputAsset> <outputAsset>JobOutputAsset(0)</outputAsset></taskBody>"
}
]
}
Response:
{
"error":{
"code":"",
"message":{
"lang":"en-US",
"value":"Parsing request content failed due to: Make sure to only use property names that are defined by the type"
}
}
}
It seems to be related with __metadata property. when I follow instruction from here : Creating Job from REST API returns a request property name error, the error changes:
"error":{
"code":"",
"message":{
"lang":"en-US",
"value":"Invalid input asset reference in TaskBody - "
}
}
}
Cant figure out whats wrong, thanks
Let me check on this, but it could be a couple issues that I have run into in the past.
First. Set both the Accept and Content-Type headers to:
"application/json; odata=verbose"
Next, double check that you are actually using the long underscore character on the metadata property. I've had issues where that was sending the wrong underscore character and it didn't match the property name.
Let me know if either of those helps.
It seems the issue was about "Content-Type". As I am using .net Core it was not easy to set the Conent-type as "application/json; odata=verbose".
1) Tried with RestSharp - dosnt support it, it cuts "odata=verbose" part out
2) Tried with Systsem.Net.Http.HttpClient -> Possible but difficult.
To add it as "Accept" :
MediaTypeWithQualityHeaderValue mtqhv;
MediaTypeWithQualityHeaderValue.TryParse("application/json;odata=verbose", out mtqhv);
client.DefaultRequestHeaders.Accept.Add(mtqhv);//ACCEPT header
To add it as "Content-Type" :
request.Content = new StringContent(content,
System.Text.Encoding.UTF8); //CONTENT-TYPE header -> default type will be text/html
request.Content.Headers.Clear(); // need to clear it - it will fail otherwise
request.Content.Headers.TryAddWithoutValidation("Content-Type","application/json;odata=verbose");

How to format the HTTP response

I have written a socket program in C. I used this program as a chat server/client using TCP. I tried to change the chat server to use it as a HTTP server by changing the port to 80. I referred to the HTTP request/response format in http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Example_session , and made my program to reply with sample response. I tried the url
http://127.0.0.1/
in browser. My program read the request and replied with response. At first, I used google-chrome. Chrome didn't load the page correctly until i added the correct data length in Content-Length header. After setting content length header, chrome loaded the page correctly. But, firefox doesn't load the page. Firefox doesn't showed any errors, but still loading the page like it is still waiting for some data. Only When i stop the server or close the socket, complete page is loaded. I tried to follow rfc2616 https://www.rfc-editor.org/rfc/rfc2616 , and made the response exactly , but the still the results are same.
Request:
GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nUser-Agent: Mozilla/5.0
(X11; Ubuntu; Linux x86_64; rv:33.0) Gecko/20100101
Firefox/33.0\r\nAccept:
text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8\r\nAccept-Language:
en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection:
keep-alive\r\n\r\n
For the above request, my program write to the socket with following response & content.
Response:
HTTP/1.1 200 OK\r\nCache-Control : no-cache, private\r\nContent-Length
: 107\r\nDate : Mon, 24 Nov 2014 10:21:21 GMT\r\n\r\n
Content:
<html><head><title></title></head><body>TIME : 1416824843 <br>DATE: Mon Nov 24 15:57:23 2014 </body></html>
This response is loading in Chrome, but not in firefox. Chrome is loading the page instantly whereas firefox is waiting for data. Note that the data length 107 is specified in the header. I donot have any addons enabled in firefox. My firefox version is in the request. Chrome version: Version 38.0.2125.111 (64-bit).
Code:
void *socket_read(void *args)
{
int socket,*s,length;
char buf[1024];
s=(int *)args;
socket=*s;
while(1){
buf[0]='\0';
length=read(socket,buf,1024);
if(length==0)
break;
else if(length==-1){
perror("Read");
return;
}
else{
printf("Request: %s\n",buf);
send_response(socket);
}
}
printf("End of read thread [%d]\n",socket);
}
int start_accept(int port)
{
int socket,csocket;
pthread_t thread;
struct sockaddr_in client;
socklen_t addrlen=sizeof(client);
pthread_attr_t attr;
socket=create_socket(port);
while(1){
if((csocket=accept(socket,(struct sockaddr *)&client,&addrlen))==-1)
{
perror("Accept");
break;
}
pthread_attr_init(&attr);
if(0!=pthread_create(&thread,&attr,socket_read,&csocket))
{
perror("Read thread");
return;
}
usleep(10000);
}
}
void send_response(int socket)
{
char buf1[1024];
int content_length;
char buf2[1024]="<html><head><title></title></head><body>TIME : 1416824843 <br>DATE: Mon Nov 24 15:57:23 2014 </body></html>";
content_length=strlen(buf2);
sprintf(buf1,"HTTP/1.1 200 OK\r\nCache-Control : no-cache, private\r\nContent-Length : %d\r\nDate : Mon, 24 Nov 2014 12:03:43 GMT\r\n\r\n",content_length);
printf("Written: %d \n",write(socket,buf1,strlen(buf1)));
fflush(stdout);
printf("Written: %d \n",write(socket,buf2,content_length));
fflush(stdout);
}
I have found the problem.
The Response is incorrect. There should not be any spaces between the header field name and colon(':'). Found this in http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 .
My correct response is
HTTP/1.1 200 OK\r\nCache-Control: no-cache, private\r\nContent-Length: 107\r\nDate: Mon, 24 Nov 2014 10:21:21 GMT\r\n\r\n
I had put a space between 'Content-Length' and ':' . That's the reason Firefox ignored the content length header and reading the socket. Chrome accepted the header fields with spaces, so the problem didn't occurred in chrome.
After removing the space, program works fine.
It actually loads the page. If you add content-type header you will see the HTML page (Content-Type: text/html; charset=UTF-8\r\n)
Anyway, both in Chrome and in Firefox you will see the connections never stops because the server doesn't close the socket. If you closed csocket, you would see the HTML page in both browsers but as you said it should be a persistent connection.