I have Netty 3.3 server with WebSockets from https://github.com/netty/netty/tree/3.2/src/main/java/org/jboss/netty/example/http/websocketx/server
I have client that implement RFC 6455, but server can't decode it's messages by default. While debugging, I see that WebSocket08FrameDecoder is used (instead WebSocket13FrameDecoder). When I downgrade client to draft00 everything works fine.
How do I configure Netty to decode RFC 6455 messages?
update
Client send this handshake package:
GET /websocket HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: 5a087
Host: 127.0.0.1
Origin: 127.0.0.1
And the handshaker on the server is WebSocketServerHandshaker13, but I still get error:
org.jboss.netty.handler.codec.frame.CorruptedFrameException: unmasked client to server frame
at org.jboss.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder.protocolViolation(WebSocket08FrameDecoder.java:350)
at org.jboss.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder.decode(WebSocket08FrameDecoder.java:138)
at org.jboss.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder.decode(WebSocket08FrameDecoder.java:56)
at org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:465)
at org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:438)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255)
at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:343)
at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:274)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:194)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)`
If you look in WebSocketServerHandshakerFactory, it instances the decoder based on the web socket version passed in the HTTP header.
public WebSocketServerHandshaker newHandshaker(HttpRequest req) {
String version = req.getHeader(Names.SEC_WEBSOCKET_VERSION);
if (version != null) {
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
return new WebSocketServerHandshaker13(webSocketURL, subprotocols, allowExtensions);
} else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) {
// Version 8 of the wire protocol - version 10 of the draft hybi specification.
return new WebSocketServerHandshaker08(webSocketURL, subprotocols, allowExtensions);
} else {
return null;
}
} else {
// Assume version 00 where version header was not specified
return new WebSocketServerHandshaker00(webSocketURL, subprotocols);
}
}
Please check your client implementation to see which version it is sending in the HTTP header. It should be Sec-WebSocket-Version: 13.
Related
I went through the other similar questions but couldn't find any solution. We have a backend service running by Spring Boot and has been working for a while now. But there is a new user of this service recently and they are using MuleSoft to send their request. But all attempts to send a file to our service fails with this error:
Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found"
The only difference we could find between a request from MuleSoft and say a curl command is that MuleSoft always sends the request with boundary value wrapped with double quotes
Mule request:
<Header name="Content-Type">multipart/form-data; charset=UTF-8; boundary="--------------------------669816398596264398718285"</Header>
Versus Postman/curl request:
* SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fd656810a00)
> POST /api/upload HTTP/2
> Host: myhost
> user-agent: curl/7.79.1
> accept: */*
> content-length: 97255
> content-type: multipart/form-data; boundary=------------------------111fd08cb4fafe1c
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
* We are completely uploaded and fine
< HTTP/2 200
< date: Mon, 19 Dec 2022 04:56:25 GMT
< content-length: 0
Our controller in Spring is very simple:
#RestController
class MyController {
#PostMapping("/upload", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
#ResponseStatus(HttpStatus.OK)
fun uploadDocument(#RequestPart("file") file: MultipartFile) {
logger.info { "ContentType: ${file.contentType}" }
logger.info { "Name: ${file.name}" }
logger.info { "Byte: ${String(file.bytes)}" }
}
}
The following curl command works fine:
curl -v -X POST -F file=#/Users/myhomefolder/Documents/some-file.jpg https://host-name/api/upload
But this script from MuleSoft doesn't (Sorry I'm not familiar with Mule, I got this code from their team):
import dw::module::Multipart
output multipart/form-data boundary = "---WebKitFormBoundary7MA4YWxkTrZu0gW"
---
{
parts : {
file : {
headers : {
"Content-Disposition" : {
"name": "file",
"filename": payload.name
},
"Content-Type" : "multipart/form-data"
},
content : payload.byteArray
}
}
}
Is there any configuration in Spring that accepts double quotes for boundary? Is there anything missing in our backend configuration that should be added to support different HTTP client?
I'm unable to perform an HTTP Post with an app running in an Android Emulator.
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content:
System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:
{ Server: Microsoft-HTTPAPI/2.0 Date: Wed, 23 Oct 2019 00:58:01
GMT Connection: close Forwarded: host=XXX.XXX.X.XX:XXXXX;
proto=https Content-Type: text/html; charset=us-ascii
Content-Length: 374 }}
Setup:
I'm using an IP address generated by Conveyor by Keyoti
I installed a security certificate on the emulator required by Conveyor by Keyoti
I swapped out Microsoft.AspNetCore.Mvc.HttpPost attribute with System.Web.Http.HttpPost
Emulator:
Successful: HTTP Get
Failed: HTTP Post
Integration Test:
Successful: HTTP Post (using same endpoint)
Code:
I wrote an automated test that calls the same HTTP Post implementation.
Because I executed the same code successfully on my laptop via an automated test, I don't think the actual code is the issue:
open Microsoft.AspNetCore.Mvc
open Newtonsoft.Json
[<ApiController>]
[<Route("api/[controller]")>]
type RegisterController () =
inherit ControllerBase()
[<System.Web.Http.HttpPost>]
member x.Post([<FromBody>] json:string) =
...
Summary:
In conclusion, I have isolated the environment to the Android Emulator and not my laptop. Hence, the emulator can successfully trigger an HTTP Get. However, it fails to perform a HTTP Post even though my laptop device can do both.
UPDATE:
I applied guidance from this Xamarin Android ASP.Net Core WebAPI document.
Specifically, I installed another security certificate on the Android emulator.
I was then able to observe an HTTP Get on the Android Emulator.
However, I continue to get an error for HTTP Post.
OperationCanceledException
Physical Device:
If I run the app from a physical android device I observe the following:
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:
{
Date: Wed, 23 Oct 2019 13:33:20 GMT
Server: Kestrel
Transfer-Encoding: chunked
Forwarded: host=xxx.xxx.x.xx:xxxxx; proto=https
Content-Type: text/plain
}}
New Update:
I disabled debugging on just my code on the server implementation and discovered the following exception:
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: 'Bad chunk size data.'
Any suggestions?
this might not be a direct answer to your question, but i would like to suggest
localtunnel. a very easy way to temporarily expose your local api so that you can test it either on emulator or even physical device. Have used this alot my self, as it is very convenient to just type a single line in terminal to start it.
The following reference solved my issue.
Infrastructure:
type GlobalHttpClient private () =
static let mutable (httpClient:System.Net.Http.HttpClient) = null
static member val Instance = httpClient with get,set
Xamarin.Android project:
using Android.Http;
using Android.Net;
using Javax.Net.Ssl;
using System.Net.Http;
using Xamarin.Android.Net;
using Xamarin.Forms;
using WebGatewaySupport;
[assembly: Dependency(typeof(HTTPClientHandlerCreationService_Android))]
namespace Android.Http
{
public class HTTPClientHandlerCreationService_Android : IHTTPClientHandlerCreationService
{
public HttpClientHandler GetInsecureHandler()
{
return new IgnoreSSLClientHandler();
}
}
internal class IgnoreSSLClientHandler : AndroidClientHandler
{
protected override SSLSocketFactory ConfigureCustomSSLSocketFactory(HttpsURLConnection connection)
{
return SSLCertificateSocketFactory.GetInsecure(1000, null);
}
protected override IHostnameVerifier GetSSLHostnameVerifier(HttpsURLConnection connection)
{
return new IgnoreSSLHostnameVerifier();
}
}
internal class IgnoreSSLHostnameVerifier : Java.Lang.Object, IHostnameVerifier
{
public bool Verify(string hostname, ISSLSession session)
{
return true;
}
}
}
Xamarin.Forms App:
switch (Device.RuntimePlatform)
{
case Device.Android:
GlobalHttpClient.Instance = new HttpClient(DependencyService.Get<IHTTPClientHandlerCreationService>().GetInsecureHandler());
break;
default:
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
GlobalHttpClient.Instance = new HttpClient(new HttpClientHandler());
break;
}
Client Gateway:
let postTo (baseAddress:string) (resource:string) (payload:Object) =
GlobalHttpClient.Instance.BaseAddress <- Uri(baseAddress)
let encoded = Uri.EscapeUriString(resource)
let result = GlobalHttpClient.Instance.PostAsJsonAsync(encoded, payload) |> toResult
result
Looks like you have a .NET Core Api. .NET Core does not have System.Web in Asp.NET. The HttpPost attribute and HttpGet attributes should come from Microsoft.AspNetCore.Mvc namespace which you have open.
Also since you are using the ApiController attribute model binding will just work as long as you bind to a model and not just a json string.
Create a model that you want the json to bind to and use that type for your parameter on Post and remove the FromBody attribute. Also if you do that you probably don't need newtonsoft.json.
open Microsoft.AspNetCore.Mvc
[<ApiController>]
[<Route("api/[controller]")>]
type RegisterController () =
inherit ControllerBase()
[<HttpPost>]
member x.Post(thing:TypeOfThing) =
This is going to be a long question..
Our company has to follow PCI Standards, so a while back we had to ensure all our Servers were TLS1.2 compliant. As a result we implemented TLS as explained here in our Xamarin Forms app. But we noticed issues in Android versions less then Api 22. So we implemented a dependency service for fetching the HTTPClient and if the Api versions were less than 22 we implemented a custom ssl socket factory, here's the example.
Everything was fine till a few weeks back there was a decision to upgrade the servers to Windows 2016 on the dev environment. We've redeployed our Web Api to the server and ever since then, the api is inaccessible from a few devices. The problem we've faced is in Samsung Galaxy S4(Android 4.4) and Nexus 5(Android 5.1.1). We've tried testing the app on a Samsung Galaxy A7(Android 6) and it works okay. iOS is also fine.
This is the error we recieve on the S4 and Nexus 5
StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content:
System.Net.Http.StreamContent, Headers: { Date: Wed, 20 Sep 2017
04:00:09 GMT Server: Microsoft-IIS/10.0 X-Android-Received-Millis:
1505880010792 X-Android-Response-Source: NETWORK 404
X-Android-Selected-Transport: http/1.1 X-Android-Sent-Millis:
1505880010781 X-Powered-By: ASP.NET Content-Length: 1245 Content-Type:
text/html
Here's the signature of the Web Api
[HttpPost("GetMinimumVersion")]
public GetMinimumVersionResponse GetMinimumVersion([FromBody] GetMinimumVersionRequest value)
And this is the code we use to make a post request
using (_httpclient = _deviceInfo.GetHttpClient())
{
_httpclient.MaxResponseContentBufferSize = 256000;
_httpclient.BaseAddress = new Uri(BaseAddress);
_httpclient.Timeout = timeout > 0 ? TimeSpan.FromSeconds(timeout) : TimeSpan.FromSeconds(60000);
Insights.Track("ApiUrlCalled", new Dictionary<string, string> { { "Api URL", url } });
var jsonOut = new StringContent(JsonConvert.SerializeObject(body, new IsoDateTimeConverter()));
jsonOut.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await _httpclient.PostAsync(url, jsonOut);
switch (response.StatusCode)
{
case HttpStatusCode.OK:
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<T>(content);
ReceiveNotificationDateTime(result);
return result;
default:
var result1 = new T { StatusID = (int)SystemStatusOutcomes.Failed, StatusMessage = response.ToString() };
ReceiveNotificationDateTime(result1);
return result1;
}
}
It's worth noting that the app when talking to the production api works fine on all devices. And we're also able to make post requests to the dev api via Postman.
After some digging and scratching, I found out that the ciphers used on production and dev were different.
Here's the cipher used on Prod
and here's the one used on dev.
I had a look at the SSL Ciphers Android supports here. And it looks like the ciper suite TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 is supported in Android Api version 20+. This makes sense that it wont work on Android 4.4. But why would we get this error on Nexus 5? Any pointers?
Also is there any workaround to get this cipher enabled on Android 4.4?
We perform PUT request to our party using CXF JAX-RS client. Request body is empty.
A simple request invocation leads to server response with code 411.
Response-Code: 411
"Content-Length is missing"
Our party's REST-server requires Content-Length HTTP-header to be set.
We switched chunking off according to note about chunking but this did not solve the problem. The REST-server still answers with 411 error.
Here is our conduit configuration from cxf.xml file
<http-conf:conduit name="{http://myhost.com/ChangePassword}WebClient.http-conduit">
<http-conf:client AllowChunking="false"/>
</http-conf:conduit>
Line in the log confirms that execution of our request bound to our conduit configuration:
DEBUG o.a.cxf.transport.http.HTTPConduit - Conduit '{http://myhost.com/ChangePassword}WebClient.http-conduit' has been configured for plain http.
Adding Content-Length header explicitly also did not help.
Invocation.Builder builder = ...
builder = builder.header(HttpHeaders.CONTENT_LENGTH, 0);
A CXF Client's log entry confirms header setting, however when we sniffed packets, we have surprisingly found that header setting has been completely ignored by CXF client. Content-Length header was not sent.
Here is the log. Content-Length header is present:
INFO o.a.c.i.LoggingOutInterceptor - Outbound Message
---------------------------
ID: 1
Address: http://myhost.com/ChangePassword?username=abc%40gmail.com&oldPassword=qwerty123&newPassword=321ytrewq
Http-Method: PUT
Content-Type: application/x-www-form-urlencoded
Headers: {Accept=[application/json], client_id=[abcdefg1234567890abcdefg12345678], Content-Length=[0], Content-Type=[application/x-www-form-urlencoded], Cache-Control=[no-cache], Connection=[Keep-Alive]}
--------------------------------------
DEBUG o.apache.cxf.transport.http.Headers - Accept: application/json
DEBUG o.apache.cxf.transport.http.Headers - client_id: abcdefg1234567890abcdefg12345678
DEBUG o.apache.cxf.transport.http.Headers - Content-Length: 0
DEBUG o.apache.cxf.transport.http.Headers - Content-Type: application/x-www-form-urlencoded
DEBUG o.apache.cxf.transport.http.Headers - Cache-Control: no-cache
DEBUG o.apache.cxf.transport.http.Headers - Connection: Keep-Alive
And here is an output of the packet sniffer. Content-Length header is not present:
PUT http://myhost.com/ChangePassword?username=abc%40gmail.com&oldPassword=qwerty123&newPassword=321ytrewq HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Accept: application/json
client_id: abcdefg1234567890abcdefg12345678
Cache-Control: no-cache
User-Agent: Apache-CXF/3.1.8
Pragma: no-cache
Host: myhost.com
Proxy-Connection: keep-alive
Does anyone know how actually disable chunking?
Here is our code:
public static void main(String[] args)
{
String clientId = "abcdefg1234567890abcdefg12345678";
String uri = "http://myhost.com";
String user = "abc#gmail.com";
Client client = ClientBuilder.newBuilder().newClient();
WebTarget target = client.target(uri);
target = target.path("ChangePassword").queryParam("username", user).queryParam("oldPassword", "qwerty123").queryParam("newPassword", "321ytrewq");
Invocation.Builder builder = target.request("application/json").header("client_id", clientId).header(HttpHeaders.CONTENT_LENGTH, 0);
Response response = builder.put(Entity.form(new Form()));
String body = response.readEntity(String.class);
System.out.println(body);
}
Versions:
OS: Windows 7 Enterprise SP1
Arch: x86_64
Java: 1.7.0_80
CXF: 3.1.8
I had a very similar issue that I was not able to solve as you did by trying to turn off chunking.
What I ended up doing was setting the Content-Length to 1 and adding some white space " " as the body. For me it seemed that the proxy servers before the server application was rejected the request and by doing that got me past the proxy servers and the server was able to process the request as it was only operating based on the URL.
I followed the User Guide available here : i added this in my pom :
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-server</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-container-grizzly</artifactId>
<version>1.2</version>
</dependency>
I wrote this in my main class :
Server server = new Server("localhost", 8624, "/", EchoEndPoint.class);
try
{
server.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Please press a key to stop the server.");
reader.readLine();
}
catch(Exception ex) { ex.printStackTrace(); }
finally
{
server.stop();
}
The content of my EchoEndPoint class is the same as described in the guide.
I tried to connect to this with a HTML5 websocket :
var ws = new WebSocket("ws://localhost:8624/echo");
It seems that, browser side, it doesn't connect (it calls the onClose callback directly). And, server side, i get this in the console :
Grave: Invalid Connection header returned: 'keep-alive'
org.glassfish.tyrus.websockets.HandshakeException: Invalid Connection header returned: 'keep-alive'
at org.glassfish.tyrus.websockets.HandShake.validate(HandShake.java:254)
at org.glassfish.tyrus.websockets.HandShake.checkForHeader(HandShake.java:246)
at org.glassfish.tyrus.websockets.HandShake.<init>(HandShake.java:97)
at org.glassfish.tyrus.websockets.draft06.HandShake06.<init>(HandShake06.java:63)
[...]
org.glassfish.grizzly.filterchain.DefaultFilterChain execute
Avertissement: Exception during FilterChain execution
java.lang.ClassCastException: org.glassfish.grizzly.http.HttpContent cannot be cast to org.glassfish.tyrus.websockets.DataFrame
at org.glassfish.tyrus.container.grizzly.WebSocketFilter.handleWrite(WebSocketFilter.java:330)
If it's of any help, i copy the request header caught with the browser inspector :
GET /echo HTTP/1.1
Host: localhost:8624
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0 FirePHP/0.7.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Sec-WebSocket-Version: 13
Origin: null
Sec-WebSocket-Key: yhGPwJ26c5fYEZ5/abvtqw==
x-insight: activate
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Is this a handshake problem ?
EDIT : i've tried in Chrome (28.0.1500.72) and it's working. Maybe the issue comes from Firefox when it builds the header ?
Tyrus is complaining about the Connection: keep-alive, Upgrade header.
Firefox isn't doing anything wrong here.
Tyrus is being too restrictive and not following the WebSocket Spec (RFC-6455) with regards to how to handle the Connection header.
The RFC states in Section 4.1:
6. The request MUST contain a |Connection| header field whose value
MUST include the "Upgrade" token.
and
3. If the response lacks a |Connection| header field or the
|Connection| header field doesn't contain a token that is an
ASCII case-insensitive match for the value "Upgrade", the client
MUST _Fail the WebSocket Connection_.
This seems like a bug in Tyrus.