Streaming a Response in Symfony2 - symfony

I am trying this example from the doc: Streaming a Response in Symfony2.
/**
* #param Request $request
* #return Response $render
* #Route("/streamedResponse", name="streamed_response")
* #Template("AcmeTestBundle::streamedResponse.html.twig")
*/
public function streamedResponseAction(Request $request)
{
$response = new StreamedResponse();
$response->setCallback(function () {
echo 'Hello World';
flush();
sleep(3);
echo 'Hello World';
flush();
});
return $response;
}
This outputs everything at the same time. Have I done something wrong?

I tried adding ob_flush() and it seems to be working. Here is my code:
public function streamedAction()
{
$response = new StreamedResponse();
$response->setCallback(function () {
echo 'Hello World';
ob_flush();
flush();
sleep(3);
echo 'Hello World';
ob_flush();
flush();
});
return $response;
}
This returns chunked transfer encoding header with chunked data. Here is output of results:
$ telnet localhost 80
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /app_dev.php/streamed HTTP/1.1
Host: symfony21.localdomain
HTTP/1.1 200 OK
Date: Wed, 12 Sep 2012 05:34:12 GMT
Server: Apache/2.2.17 (Unix) DAV/2 mod_ssl/2.2.17 OpenSSL/0.9.8o
cache-control: no-cache, private
x-debug-token: 50501eda7d437
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
b
Hello World
b
Hello World
0
Connection closed by foreign host.
If you see this response in browser, it will display "HelloWorldHelloWorld" after about 3 seconds loading as browser will wait until all chunked data is received as Content-Type is text/*, but when you see the network stream, it is actually doing streaming by sending chunked data.

Related

springboot2 actuator endpoint not supporting Server Sent Event(SSE)

I am trying to expose few metrics as a stream using SSE. I am able to consume the SSE event from the restController but when I added custom actuator endpoint , it just closes the connection right way.
#Component
#Endpoint(id = "test")
public class StreamMetrics {
#ReadOperation
public Flux<ServerSentEvent<String>> streamEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> ServerSentEvent.<String> builder()
.id(String.valueOf(sequence))
.event("pingpong")
.data("ping")
.build());
}
}
Result
curl -n -v http://localhost:9080/actuator/test
* Trying ::1:9080...
* TCP_NODELAY set
* Connected to localhost (::1) port 9080 (#0)
> GET /actuator/test HTTP/1.1
> Host: localhost:9080
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< Content-Type: text/event-stream;charset=UTF-8
<
id:0
event:pingpong
data:ping
* Connection #0 to host localhost left intact
this is terminated right after the fist event
where as
#RestController
#RequestMapping(value = "/test")
public class SSETest {
#GetMapping("/stream-sse")
public Flux<ServerSentEvent<String>> streamEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> ServerSentEvent.<String> builder()
.id(String.valueOf(sequence))
.event("pingpong")
.data("ping")
.build());
}
}
Result
curl -v -n http://localhost:9080/test/stream-sse
* Trying ::1:9080...
* TCP_NODELAY set
* Connected to localhost (::1) port 9080 (#0)
> GET /test/stream-sse HTTP/1.1
> Host: localhost:9080
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< Content-Type: text/event-stream;charset=UTF-8
<
id:0
event:pingpong
data:ping
id:1
event:pingpong
data:ping
id:2
event:pingpong
data:ping
this goes on without getting terminated.
What is special about endpoint annotation that is terminating the event (continuous flow)?
I tested this in '2.2.4 ' and '2.3.0'
I found an answer. #ReadOperation(produces = "text/event-stream") will alter the default content type application/vnd.spring-boot.actuator.v3+json in actuator endpoints. There was not a document so I looked at the code.
#Component
#Endpoint(id = "test")
public class StreamMetrics {
#ReadOperation(produces = "text/event-stream")
public Flux<ServerSentEvent<String>> streamEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> ServerSentEvent.<String> builder()
.id(String.valueOf(sequence))
.event("pingpong")
.data("ping")
.build());
}
}

How do I connect to HTTP server with content type after requesting a specific file from directory?

I wrote a function that checks for file type (using Content Type) for HTTP server. However, I have this as global variable, but I blocked out the HTTP and content-type lines.
char * request_str; //= "HTTP/1.0 200 OK\r\n"
//"Content-type: text/html; charset=UTF-8\r\n\r\n";
The function that checks for file types as such as html, png, txt, etc. It has filename, filetype, and request_str as arguments. I'm hoping the request_str would return after reading the filename/filetype, but I guess it doesn't work.
void get_filetype(char *filename, char *filetype, char *request_str)
{
if(strstr(filename, ".html"))
{
strcpy(filetype, "text/html");
request_str = "HTTP/1.0 200 OK\r\n"
"Content-type: text/html; charset=UTF-8\r\n\r\n";
return request_str;
}
else if(strstr(filename, ".gif"))
{
strcpy(filetype, "image/gif");
request_str = "HTTP/1.0 200 OK\r\n"
"Content-type: image/gif; charset=UTF-8\r\n\r\n";
return request_str;
}
else if(strstr(filename, ".png"))
{
strcpy(filetype, "image/png");
request_str = "HTTP/1.0 200 OK\r\n"
"Content-type: image/png; charset=UTF-8\r\n\r\n";
return request_str;
}
else if(strstr(filename, ".jpg"))
{
strcpy(filetype, "image/jpg");
request_str = "HTTP/1.0 200 OK\r\n"
"Content-type: image/jpg; charset=UTF-8\r\n\r\n";
return request_str;
}
else
{
strcpy(filetype, "text/plain");
request_str = "HTTP/1.0 200 OK\r\n"
"Content-type: text/plain; charset=UTF-8\r\n\r\n";
return request_str;
}
}
I have this function in the server request function when it attempts to connect to server. The problem I'm approaching is it wouldn't connect to the web server when I test it on my browser so I cannot see anything. Am I supposed to check the file type before attemtpting to connect to the server? Thank you guys in advance.
while(1){
file_offset += recv(client_fd,&client_buf[file_offset],4096,0);
if(strstr(client_buf,"\r\n\r\n"))
break;
}
requested_file = parseRequest(client_buf);
if(stat(filename, &for_filename) < 0)
{
clienterror(client_fd, filename, "404", "Not found", "Webserver couldn't find this file");
return;
}
get_filetype(filename, file_type, request_str);
send(client_fd,request_str,strlen(request_str),0);
// take requested_file, add a . to beginning, open that file
filename[0] = '.';
strncpy(&filename[1],requested_file,4095);
read_fd = open(filename,0,0);
Am I supposed to check the file type before attemtpting to connect to
the server?
First of all, unless you're doing it as an exercise, you should really use an HTTP client library instead of building request strings yourself.
Second, in HTTP, you can specify multiple accepted formats in a single request. Browsers use a list of default values which you can see here. For instance, when Firefox doesn't know in advance the type of data it's supposed to request, it uses the string text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8. If it knows that it's a video, then it uses a different string audio/webm, audio/ogg, audio/wav, audio/*;q=0.9, application/ogg;q=0.7, video/*;q=0.6; */*;q=0.5. There are additional request string for other types such as images, and scripts.

Arduino mega + esp 8266 sending get request

i have a php script which help to store data into google firebase.
i am using this url to access my php scipt and input the data:
arduino.byethost22.com/FirebaseTest.php?slot1_data=empty&slot2_data=occupied
i have tried it and it is able to store slot1_data as empty and slot2_data as occupied. However i need to use arduino to send this url. i am using this code currently
#include "SoftwareSerial.h"
#define DEBUG false // turn debug message on or off in serial
String server = "arduino.byethost22.com";
String uri = "/FirebaseTest.php?slot1_data=empty&slot2_data=occupied";
void setup() {
Serial3.begin(115200); //serial3 for esp8266
Serial.begin(115200);
sendData("AT+RST\r\n",2000,DEBUG); // reset module
sendData("AT+CWMODE=3\r\n",1000,DEBUG); // configure as access point
//sendData("AT+CWJAP=\"WifiName\",\"Password\"\r\n",3000,DEBUG);
//delay(20000);
sendData("AT+CIFSR\r\n",1000,DEBUG); // get ip address
sendData("AT+CIPMUX=0\r\n",1000,DEBUG); // configure for single connections
}
void loop() {
Serial3.println("AT+CIPSTART=\"TCP\",\"" + server + "\",80");//start a TCP connection.
if( Serial3.find("OK")) {
Serial.println("TCP connection ready");
}
delay(1000);
String getRequest = "GET " + uri + " HTTP/1.1\r\n" +
"Host: " + server + "\r\n\r\n";
String sendCmd = "AT+CIPSEND=";//determine the number of caracters to be sent.
Serial3.print(sendCmd);
Serial3.println(getRequest.length() );
delay(500);
if(Serial3.find(">")) {
Serial.println("Sending..");
}
Serial3.print(getRequest);
if( Serial3.find("SEND OK")) {
Serial.println("Packet sent");
}
while (Serial3.available()) {
String tmpResp = Serial3.readString();
Serial.println(tmpResp);
}
delay(20000);
}
String sendData(String command, const int timeout, boolean debug)
{
String response = "";
Serial3.print(command); // send the read character to the esp8266
long int time = millis();
while( (time+timeout) > millis())
{
while(Serial3.available())
{
// The esp has data so display its output to the serial window
char c = Serial3.read(); // read the next character.
response+=c;
}
}
//if(debug)
//{
Serial.print(response);
//}
return response;
}
it seems that it have problem sending the get request to the php script.
i am getting packet sent in the serial monitor but nothing changed in the firebase data
i am also getting
+IPD,1104:HTTP/1.1 200 OK
Server: nginx
Date: Sat, 15 Oct 2016 09:21:34 GMT
Content-Type: text/html
Content-Length: 875
Connection: keep-alive
Vary: Accept-Encoding
Expires: Thu, 01 Jan 1970 00:00:01 GMT
Cache-Control: no-cache
<html><body><script type="text/javascript" src="/aes.js" ></script><script>function toNumbers(d){var e=[];d.replace(/(..)/g,function(d){e.push(parseInt(d,16))});return e}function toHex(){for(var d=[],d=1==arguments.length&&arguments[0].constructor==Array?arguments[0]:arguments,e="",f=0;f<d.length;f++)e+=(16>d[f]?"0":"")+d[f].toString(16);return e.toLowerCase()}var a=toNumbers("f655ba9d09a112d4968c63579db590b4"),b=toNumbers("98344c2eee86c3994890592585b49f80"),c=toNumbers("b5ebc3b806c39a4a7fc1e4cecb45feab");document.cookie="__test="+toHex(slowAES.decrypt(c,2,a,b))+"; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/"; location.href="http://arduino.byethost22.com/FirebaseTest.php?slot1_data=0&slot2_data=1&i=1";</script><noscript>This site requires Javascript to work, please enable Javascript in your browser or use a browser with Javascript support</noscript></body></html>
It asked me to enable javascript in my browser, but i am using arduino, how do i do it?
When i key in the same uri in google chrome, the data can be updated
how do i solve this problem?
My code finally can work and send data online. i change to use 000webhost as my host server instead of byethost and the data manage to be updated.
i dont really know why but i guess that byethost dont support javascript.

XCode 8, cannot receive push notification

After upgrade to XCode 8, my iphone with IOS 10 cannot receive push notification any more.
I have enable the "Automatically manage signing".
I re-generate the certificate from Apple website, the universal one.
My local codes is running without any error.
I can get device token correctly with the codes (swift 3):
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
print("start didFinishLaunchingWithOptions")
// Register the supported interaction types.
let types = UIUserNotificationType([.alert, .badge, .sound])
let mySettings = UIUserNotificationSettings(types: types, categories: nil)
UIApplication.shared.registerUserNotificationSettings(mySettings)
// Register for remote notifications.
UIApplication.shared.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("start didRegisterForRemoteNotificationsWithDeviceToken")
let deviceTokenString = ((deviceToken as NSData).description.trimmingCharacters(in: CharacterSet(charactersIn: "<>")) as NSString).replacingOccurrences(of: " ", with: "")
....
}
My PHP in server side always return 200 which is successful.
$ch = curl_init();
if (!defined('CURL_HTTP_VERSION_2_0')) {
define('CURL_HTTP_VERSION_2_0', 3);
}
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
$pem_file = '/var/my.pem';
$pem_secret = 'xxxxxx';
$apns_topic = 'xxxxxx';
$apns_expiration = 2;
$change_alert = "{
\"aps\": {
\"alert\": \"$alert_message\",
\"badge\": $badge,
\"sound\": \"default\"
},
\"xxxxxxxx\": \"$xxxxxx\"
}";
$url = "https://api.development.push.apple.com/3/device/$device_token";
//$url = "https://api.push.apple.com/3/device/$device_token";
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POSTFIELDS, $change_alert);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("apns-topic: $apns_topic", "apns-expiration: $apns_expiration"));
curl_setopt($ch, CURLOPT_SSLCERT, $pem_file);
//curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $pem_secret);
$response = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
My apns-topic is the same with my app bundle id.
Any idea for this issue?
BTW, XCode 8 is not as stable as the previous version.
===============
CURL logs:
* Trying 17.188.135.156...
* Connected to api.push.apple.com (17.188.135.156) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:#STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* SSL connection using TLSv1.2 / ECxxxxxxxxxxxxxxxxxx
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=api.push.apple.com; OU=management:idms.group.533599; O=Apple Inc.; ST=California; C=US
* start date: Aug 28 19:03:46 2015 GMT
* expire date: Sep 26 19:03:46 2017 GMT
* subjectAltName: host "api.push.apple.com" matched cert's "api.push.apple.com"
* issuer: CN=Apple IST CA 2 - G1; OU=Certification Authority; O=Apple Inc.; C=US
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* TCP_NODELAY set
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7f2d6eda3970)
> POST /3/device/3xxxxxxxxxxxxxxxxxx HTTP/1.1
Host: api.push.apple.com
Accept: */*
apns-topic: xxxxxxxxxx
apns-expiration: 0
Content-Length: 316
Content-Type: application/x-www-form-urlencoded
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
* We are completely uploaded and fine
< HTTP/2.0 200
< apns-id:AF034xxxxxxxxxxxx0F9F27
<
* Connection #0 to host api.push.apple.com left intact

I am getting cookie when I am using wget but no cookies when using groovy script

Here is the wget command :
wget --auth-no-challenge --http-user=auth_user --http-password=auth_pass --save-cookies cookies.txt --keep-session-cookies --post-data 'user_login=username&pass=password' https://www.helloworld.com/login.php
Here is the groovy script:
static List<String> login(String baseUrl, String path, query, method = Method.POST) {
try {
def ret = null
def http = new HTTPBuilder(baseUrl)
http.request(method, ContentType.URLENC) {
uri.path = path
uri.query = query
headers.'Authorization' = "Basic ${"auth_user:auth_pass".bytes.encodeBase64().toString()}"
// response handler for a success response code
response.success = { resp, data ->
println "response status: ${resp.statusLine}"
resp.getHeaders('Set-Cookie').each {
def cookie = it.value.split(";").toString()
println cookie
.add(cookie)
}
println 'Response : ' + resp.getData()
}
}
return cookies
} catch (groovyx.net.http.HttpResponseException ex) {
ex.printStackTrace()
} catch (java.net.ConnectException ex) {
ex.printStackTrace()
}
}
def url = "https://www.helloworld.com/login.php"
def path = ""
def query = [ user_login: "username#calypso", pass: "password" ]
// Submit a request via POST
def response = login(url, path, query)
So wget command get me cookie but this groovy script doesn't. This is the response I am getting:
response status: HTTP/1.1 200 OK
Header:
Date : Mon, 28 Mar 2016 19:56:20 GMT
Server : Apache/2.2.3 (CentOS)
Last-Modified : Sat, 03 May 2003 21:37:01 GMT
ETag : "56b0013-1d4-3bcc9546cd940"
Accept-Ranges : bytes
Content-Length : 468
Keep-Alive : timeout=999
Connection : Keep-Alive
Content-Type : text/html; charset=UTF-8
You probably should not do authentication by yourself. If only successful authentication creates a cookie this could explain the phenomenon.
Try
def http = new HTTPBuilder(baseUrl)
http.auth.basic 'auth_user', 'auth_pass'
See also the documentation. Additionally the non-empty response body might give you a hint about what's going wrong.
I think the problem is that you are calling toString on your array after splitting. Try getting the first value of the array:
String cookie = it.value.split(';')[0]
Is it possible that the cookie is httpOnly and your groovy script is honoring that, but wget is not ?

Resources