I've set up an Stomp endpoint in Java using Spring boot like this:
#Configuration
#EnableWebSocketMessageBroker
public class ChatConfig implements WebSocketMessageBrokerConfigurer
{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry)
{
registry.addEndpoint("/ws").setAllowedOriginPatterns("*"); // connection point
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry)
{
registry.setApplicationDestinationPrefixes("/app"); // sender point
registry.enableSimpleBroker("/user"); // listener point
}
}
I'm trying to connect to this STOMP server(via LAN) using java.net.socket. I've read through STOMP specification here Stomp specification. What I've written so far in client side:
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("my computer Ipv4 address", 8080);
String frame = "CONNECT\n"
+ "accept-version:1.2\n\n"
+ "host:localhost\0";
OutputStream os = socket.getOutputStream();
os.write(frame.getBytes(StandardCharsets.UTF_8));
InputStream is = socket.getInputStream();
String answer = new String(is.readAllBytes());
System.out.print(answer);
}
It returns this html code:
HTTP/1.1 400
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 435
Date: Thu, 16 Feb 2023 10:15:41 GMT
Connection: close
<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1></body></html>
Server returns this:
Invalid character found in method name [CONNECT0x0aaccept-version:1.20x0a0x0ahost:192.168.1.1760x00...]. HTTP method names must be token
I'm quite a noob in network programming. There's a library for this, but I want to learn more about STOMP. So, I hope you guys can help me out with this.
Related
I've created a route to allow me to forward a REST call. Everything is going well, except I cannot modify the HTTP headers of the response (actually I can't even get them untouched on the response).
// My processor
private void remplacerLiensDansHeader(final Exchange exchange, final String rootUrlPivotJoram, final String rootUrlRemplacement) {
// That is OK, I get the values just fine
ArrayList<String> oldLinks = exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
// This is also OK
final List<String> newLinks = anciensLiens.stream().map(lien -> lien.replace(rootUrlPivotJoram, rootUrlRemplacement)).collect(toList());
// No error, but apparently doesn't work
exchange.getMessage().setHeader(HEADER_LINK, newLinks);
exchange.getMessage().setHeader("test", "test");
}
// Route configuration
#Override
public void configure() throws Exception {
this.from(RestRouteBuilder.endPoint(createProducerJoramConfiguration))
.setExchangePattern(InOut)
.removeHeader(Exchange.HTTP_URI)
.toD(createProducerJoramConfiguration.getUrlDestination())
.setHeader("test", () -> "test") // that doesn't work either
.process(createProducerJoramConfiguration.getProcessor()); // this is the processor with the code above
}
This is the response I get (note that the response code is 200 and I think it's weird as the original is 201)
curl -v -XPost --user "xxx:yyyy" http://localhost:10015/zzzz/webservices/xxxxx
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 10015 (#0)
* Server auth using Basic with user 'xxx'
> Post /zzzzz/webservices/eeee HTTP/1.1
> Host: localhost:10015
> Authorization: Basic pppppppppppppppppp==
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200
< Date: Tue, 31 Aug 2021 11:17:49 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
<
* Connection #0 to host localhost left intact
Two things I've noticed:
if I add a body in the processor, then the body is present in the response,
if I remove the processor, the headers present in the "original response" are not present either.
I don't know what headers you exactly lose, but be aware that the Camel HTTP component has a default header filter (as lots of Camel components have).
If you don't specify your own HeaderFilterStrategy, the default HttpHeaderFilterStrategy is used.
This default filters the following headers:
content-length
content-type
host
cache-control
connection
date
pragma
trailer
transfer-encoding
upgrade
via
warning
Camel*
org.apache.camel*
With this filter, Camel wants to avoid that old HTTP headers are still present on outgoing requests (probably with wrong data).
The filtering of Camel headers is just to remove Camel specific stuff that is not relevant for HTTP.
Actually, the problem was the cxfrs component.
We manage to find an answer here : see : Response to REST client from Camel CXFRS route?
Here is the final solution.
Thanks to everyone that looked or answer, I hope that'll help someone else.
public class ModificationHeaderLinkProcessor implements Processor {
private static final String HEADER_LINK = "Link";
#Override
public void process(final Exchange exchange) {
List<String> newLinks= getNewLinks(exchange, oldUrl, newUrl);
ResponseBuilder builder = createHttpResponse(exchange);
builder.header(HEADER_LINK, newLinks);
exchange.getIn().setBody(builder.build());
}
private List<String> getNewLinks(final Exchange exchange, final String oldUrl, final String newUrl) {
ArrayList<String> oldLinks= exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
return oldLinks.stream().map(link-> link.replace(oldUrl, newUrl)).collect(toList());
}
private ResponseBuilder createHttpResponse(final Exchange exchange) {
ResponseBuilder builder = Response.status(getHttpStatusCode(exchange))
.entity(exchange.getIn().getBody());
clearUselessHeader(exchange);
exchange.getIn().getHeaders().forEach(builder::header);
return builder;
}
private void clearUselessHeader(final Exchange exchange) {
exchange.getIn().removeHeader(HEADER_LINK);
exchange.getIn().removeHeaders("Camel*");
}
private Integer getHttpStatusCode(final Exchange exchange) {
return exchange.getIn().getHeader(exchange.HTTP_RESPONSE_CODE, Integer.class);
}
private final String getPropertiesValue(CamelContext camelContext, String key) {
return camelContext.getPropertiesComponent().resolveProperty(key).orElseThrow();
}
}
I'm trying to connect to my websocket from a different domain.
The server is on localhost:8098 and the client is on localhost:8080.
Everytime i try to connect i get a 'Access-Control-Allow-Origin' error, i also added the .setAllowedOrigins("*").
Not sure what's missing.
Server
#Configuration
#EnableWebSocketMessageBroker
public class webSocketObjects implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws/Object").setAllowedOrigins("*").withSockJS();
}
Client
<script>
let stompClient=null;
import Stomp from 'stompjs';
import SockJS from 'sockjs-client'
export default {
name: "modal",
props: ['node'],
data() {
return{
bacnetObject: '',
status: "disconnected"
}
},
mounted() {
this.bacnetObject = this.node;
},
methods: {
connect: function(){
const socket = new SockJS('http://localhost:8098/ws/Object');
stompClient = Stomp.over(socket);
stompClient.connect({
}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/user', console.log(String.body))
})
},
disconnect: function () {
stompClient.disconnect();
}
}
}
</script>
Error I am getting:
Access to XMLHttpRequest at 'http://localhost:8098/ws/Object/info?t=1571728150435' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If you're using credentials, * would not work according to the documentation at MDN. Try to specify the host instead.
For requests without credentials, the literal value "*" can be specified, as a wildcard; the value tells browsers to allow requesting code from any origin to access the resource. Attempting to use the wildcard with credentials will result in an error.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
On the other hand, sometimes depending on your case, you may need to take care of these headers
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: header1, header2 ...
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests
I work on small test project to check how Spring Reactive Web Applications actually works with MongoDB.
I follow the manual from https://docs.spring.io/spring/docs/5.0.0.M4/spring-framework-reference/html/web-reactive.html
and it states that I can process POST request in controller like:
#PostMapping("/person")
Mono<Void> create(#RequestBody Publisher<Person> personStream) {
return this.repository.save(personStream).then();
}
Though this seems not works. Here the controller I implemented:
https://github.com/pavelmorozov/reactor-poc/blob/master/src/main/java/com/springreactive/poc/controller/BanquetHallController.java
it have just one POST mapping and it is very simple:
#PostMapping("/BanquetHall")
Mono<Void> create(#RequestBody Publisher<BanquetHall> banquetHallStream) {
return banquetHallRepository.insert(banquetHallStream).then();
}
It is called each time I issue a POST with curl:
curl -v -XPOST -H "Content-type: application/json" -d '{"name":"BH22"}' 'http://localhost:8080/BanquetHall'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /BanquetHall HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> Accept: */*
> Content-type: application/json
> Content-Length: 15
>
* upload completely sent off: 15 out of 15 bytes
< HTTP/1.1 200 OK
< content-length: 0
<
* Connection #0 to host localhost left intact
And I see new objects stored in mongodb, but they not contain data. To debug I build simple subscriber, to see the data actually passed as request body to controller:
Subscriber s = new Subscriber() {
#Override
public void onSubscribe(Subscription s) {
logger.info("Argument: "+s.toString());
}
#Override
public void onNext(Object t) {
logger.info("Argument: "+t.toString());
}
#Override
public void onError(Throwable t) {
logger.info("Argument: "+t.toString());
}
#Override
public void onComplete() {
logger.info("Complete! ");
}
};
banquetHallStream.subscribe(s);
and now I see after subscription onError method called. The Throwable states body missing:
Here error string:
Request body is missing: reactor.core.publisher.Mono<java.lang.Void> com.springreactive.poc.controller.BanquetHallController.create(org.reactivestreams.Publisher<com.springreactive.poc.domain.BanquetHall>)
Why request body is empty?
Also good to know: As I new with all this reactive stuff, could it be some better approach to debug Publisher/Subscriber without manual implementing Subscriber?
Update I updated POST handler method description and it passes request body as String object:
Mono<Void> create(#RequestBody String banquetHallStream)
Then this is not a "Reactive", right? String is not reactive, as Publisher should be...
I had exact the same issue and was able to solve it by putting #ResponseStatus on method. Below is how method controller looks like:
#ResponseStatus(HttpStatus.CREATED)
#PostMapping(value = "/bulk", consumes = APPLICATION_STREAM_JSON_VALUE)
public Mono<Void> bulkInsert(#RequestBody Flux<Quote> quotes) {
return quoteReactiveRepository.insert(quotes).then();
}
I'm doing the request to that endpoint using WebClient:
webClient.post()
.uri("/quotes/bulk")
.contentType(MediaType.APPLICATION_STREAM_JSON)
.body(flux(), Quote.class)
.retrieve()
.bodyToMono(Void.class).block();
tested with: org.springframework.boot:spring-boot-starter-webflux:2.1.0.RELEASE
I am using below system properties:
System.setProperty("com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.sun.xml.ws.transport.http.HttpAdapter.dump", "true");
System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");
This is printing:
---[HTTP response - https://XXXXXXXXXXXXXXXXX/v1.0?wsdl - 200]---
null: HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Language: en-US
Content-Type: text/xml; charset=utf-8
Date: Tue, 09 Jan 2018 12:23:42 GMT
Keep-Alive: timeout=30, max=100
Transfer-Encoding: chunked
X-Frame-Options: SAMEORIGIN
X-Powered-By: Servlet/3.0
<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><sch:InquiryResponse xmlns:sch="http://XXXXXXXXXX/1.0"><sch:InquiryResponseHeader><sch:ClientID>XXXX</sch:ClientID><sch:ProductCode>PCS</sch:ProductCode><sch:SuccessCode>0</sch:SuccessCode><sch:Date>XX-XX-XXXX</sch:Date><sch:Time>17:53:28</sch:Time></sch:InquiryResponseHeader><sch:InquiryRequestInfo><sch:InquiryPurpose>05</sch:InquiryPurpose><sch:FirstName>Ajay</sch:FirstName><sch:LastName>XXXX</sch:LastName><sch:AddrLine1>XXXX</sch:AddrLine1><sch:State>MH</sch:State><sch:Postal>411014</sch:Postal><sch:DOB>1987-06-21</sch:DOB><sch:Id>XXXX</sch:Id><sch:MobilePhone>XXXX</sch:MobilePhone></sch:InquiryRequestInfo><sch:ReportData><sch:Error><sch:ErrorCode>E0021</sch:ErrorCode><sch:ErrorMsg>User ID does not exist for the given customer.</sch:ErrorMsg></sch:Error></sch:ReportData></sch:InquiryResponse></soapenv:Body></soapenv:Envelope>
How can I get XML data from above printing data?
I want to store that XML in a variable for further use.
Thanks.
MessageHandler:
Utilizing the extensible Handler framework provided by JAX-WS Specification and the better Message abstraction in RI, we introduced a new handler called MessageHandler to extend your Web Service applications. MessageHandler is similar to SOAPHandler, except that implementations of it gets access to MessageHandlerContext (an extension of MessageContext). Through MessageHandlerContext one can access the Message and process it using the Message API. As I put in the title of the blog, this handler lets you work on Message, which provides efficient ways to access/process the message not just a DOM based message. The programming model of the handlers is same and the Message handlers can be mixed with standard Logical and SOAP handlers. I have added a sample in JAX-WS RI 2.1.3 showing the use of MessageHandler to log messages and here is a snippet from the sample:
public class LoggingHandler implements MessageHandler<MessageHandlerContext> {
public boolean handleMessage(MessageHandlerContext mhc) {
Message m = mhc.getMessage().copy();
XMLStreamWriter writer = XMLStreamWriterFactory.create(System.out);
try {
m.writeTo(writer);
} catch (XMLStreamException e) {
e.printStackTrace();
return false;
}
return true;
}
public boolean handleFault(MessageHandlerContext mhc) {
.....
return true;
}
public void close(MessageContext messageContext) { }
public Set getHeaders() {
return null;
}
}
i'm trying to make a RESTful WS to upload a file with CXFRS camel component, i'm trying to retrive the uploaded file via getAttachment method, but it is always empty.
This is my code:
EndPoint class: ExposedApi.java
#Path("/test")
public class ExposedApi {
#POST
#Path("/resources/solver")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public Response upload(#Multipart(value = "file") Attachment attachments ){
return null;
}
}
Code that extends routebuolder: RouteConf.java
public class RouteConf extends RouteBuilder {
#Override
public void configure() throws Exception {
// TODO Auto-generated method stub
getContext().setTracing(true);
from("cxfrs://http://localhost:9090/test?resourceClasses=org.foo.ExposedApi")
.streamCaching()
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
Message inMessage = exchange.getIn();
String operationName = inMessage.getHeader(CxfConstants.OPERATION_NAME, String.class);
if (operationName=="upload"){
Map<String, DataHandler> names= inMessage.getAttachments();
exchange.getOut().setBody(inMessage.getAttachmentNames().toString()+ " TEST "+ names.keySet().toString());
}
}
});
}
}
Curl request:
curl -v -F "file=#/Users/Massimo/Desktop/ic_eb.png" http://localhost:9090/test/test/resources/solver
and this is the response
* Adding handle: conn: 0x7f92cb804000
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7f92cb804000) send_pipe: 1, recv_pipe: 0
* About to connect() to localhost port 9090 (#0)
* Trying ::1...
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9090 (#0)
> POST /test/test/resources/solver HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:9090
> Accept: */*
> Content-Length: 89280
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=----------------------------837830fae872
>
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Fri, 10 Oct 2014 14:54:51 GMT
< Content-Length: 10
* Server Jetty(7.6.9.v20130131) is not blacklisted
< Server: Jetty(7.6.9.v20130131)
<
* Connection #0 to host localhost left intact
[] TEST []
What am i doing wrong? how i can retrive the attached file?
after a bit of work i've find the solution.
the attachment is inside the body.
If you want retrive the attachment this line of code worked for me
Attachment att = (Attachment)inMessage.getBody(ArrayList.class).get(0);