I'm trying to modify a custom web server app to work with HTML5 video.
It serves a HTML5 page with a basic <video> tag and then it needs to handle the requests for actual content.
The only way I could get it to work so far is to load the entire video file into the memory and then send it back in a single response. It's not a practical option. I want to serve it piece by piece: send back, say, 100 kb, and wait for the browser to request more.
I see a request with the following headers:
http_version = 1.1
request_method = GET
Host = ###.###.###.###:##
User-Agent = Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0
Accept = video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5
Accept-Language = en-US,en;q=0.5
Connection = keep-alive
Range = bytes=0-
I tried to send back a partial content response:
HTTP/1.1 206 Partial content
Content-Type: video/mp4
Content-Range: bytes 0-99999 / 232725251
Content-Length: 100000
I get a few more GET requests, as follows
Cache-Control = no-cache
Connection = Keep-Alive
Pragma = getIfoFileURI.dlna.org
Accept = */*
User-Agent = NSPlayer/12.00.7601.17514 WMFSDK/12.00.7601.17514
GetContentFeatures.DLNA.ORG = 1
Host = ###.###.###.###:##
(with no indication that the browser wants any specific part of the file.) No matter what I send back to the browser, the video does not play.
As stated above, the same video will play correctly if I try to send the entire 230 MB file at once in the same HTTP packet.
Is there any way to get this all working nicely through partial content requests? I'm using Firefox for testing purposes, but it needs to work with all browsers eventually.
I know this is an old question, but if it helps you can try the following "Model" that we use in our code base.
class Model_DownloadableFile {
private $full_path;
function __construct($full_path) {
$this->full_path = $full_path;
}
public function get_full_path() {
return $this->full_path;
}
// Function borrowed from (been cleaned up and modified slightly): http://stackoverflow.com/questions/157318/resumable-downloads-when-using-php-to-send-the-file/4451376#4451376
// Allows for resuming paused downloads etc
public function download_file_in_browser() {
// Avoid sending unexpected errors to the client - we should be serving a file,
// we don't want to corrupt the data we send
#error_reporting(0);
// Make sure the files exists, otherwise we are wasting our time
if (!file_exists($this->full_path)) {
header('HTTP/1.1 404 Not Found');
exit;
}
// Get the 'Range' header if one was sent
if (isset($_SERVER['HTTP_RANGE'])) {
$range = $_SERVER['HTTP_RANGE']; // IIS/Some Apache versions
} else if ($apache = apache_request_headers()) { // Try Apache again
$headers = array();
foreach ($apache as $header => $val) {
$headers[strtolower($header)] = $val;
}
if (isset($headers['range'])) {
$range = $headers['range'];
} else {
$range = false; // We can't get the header/there isn't one set
}
} else {
$range = false; // We can't get the header/there isn't one set
}
// Get the data range requested (if any)
$filesize = filesize($this->full_path);
$length = $filesize;
if ($range) {
$partial = true;
list($param, $range) = explode('=', $range);
if (strtolower(trim($param)) != 'bytes') { // Bad request - range unit is not 'bytes'
header("HTTP/1.1 400 Invalid Request");
exit;
}
$range = explode(',', $range);
$range = explode('-', $range[0]); // We only deal with the first requested range
if (count($range) != 2) { // Bad request - 'bytes' parameter is not valid
header("HTTP/1.1 400 Invalid Request");
exit;
}
if ($range[0] === '') { // First number missing, return last $range[1] bytes
$end = $filesize - 1;
$start = $end - intval($range[0]);
} else if ($range[1] === '') { // Second number missing, return from byte $range[0] to end
$start = intval($range[0]);
$end = $filesize - 1;
} else { // Both numbers present, return specific range
$start = intval($range[0]);
$end = intval($range[1]);
if ($end >= $filesize || (!$start && (!$end || $end == ($filesize - 1)))) {
$partial = false;
} // Invalid range/whole file specified, return whole file
}
$length = $end - $start + 1;
} else {
$partial = false; // No range requested
}
// Determine the content type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$contenttype = finfo_file($finfo, $this->full_path);
finfo_close($finfo);
// Send standard headers
header("Content-Type: $contenttype");
header("Content-Length: $length");
header('Content-Disposition: attachment; filename="' . basename($this->full_path) . '"');
header('Accept-Ranges: bytes');
// if requested, send extra headers and part of file...
if ($partial) {
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes $start-$end/$filesize");
if (!$fp = fopen($this->full_path, 'r')) { // Error out if we can't read the file
header("HTTP/1.1 500 Internal Server Error");
exit;
}
if ($start) {
fseek($fp, $start);
}
while ($length) { // Read in blocks of 8KB so we don't chew up memory on the server
$read = ($length > 8192) ? 8192 : $length;
$length -= $read;
print(fread($fp, $read));
}
fclose($fp);
} else {
readfile($this->full_path); // ...otherwise just send the whole file
}
// Exit here to avoid accidentally sending extra content on the end of the file
exit;
}
}
You then use it like this:
(new Model_DownloadableFile('FULL/PATH/TO/FILE'))->download_file_in_browser();
It will deal with sending part of the file or the full file etc and works well for us in this and lots of other situations. Hope it helps.
I want partial range requests, because I'll be doing realtime transcoding, I can't have the file completely transcoded and available upon request.
For response which you don't know the full body content yet (you can't guess the Content-Length, live encoding), use chunk encoding:
HTTP/1.1 200 OK
Content-Type: video/mp4
Transfer-Encoding: chunked
Trailer: Expires
1E; 1st chunk
...binary....data...chunk1..my
24; 2nd chunk
video..binary....data....chunk2..con
22; 3rd chunk
tent...binary....data....chunk3..a
2A; 4th chunk
nd...binary......data......chunk4...etc...
0
Expires: Wed, 21 Oct 2015 07:28:00 GMT
Each chunk is send when it's available: when few frames are encoded or when the output buffer is full, 100kB are generated, etc.
22; 3rd chunk
tent...binary....data....chunk3..a
Where 22 give the chunk byte length in hexa (0x22 = 34 bytes),
; 3rd chunk is extra chunk infos (optional) and tent...binary....data....chunk3..a is the content of the chunk.
Then, when the encoding is finished and all chunks are sent, end by:
0
Expires: Wed, 21 Oct 2015 07:28:00 GMT
Where 0 means there not more chunks, followed by zero or more trailer (allowed header fields) defined in the header (Trailer: Expires and Expires: Wed, 21 Oct 2015 07:28:00 GMT are not required) to provide checksums or digital signatures, etc.
Here is the equivalent of the server's response if the file was already generated (no live encoding):
HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Length: 142
Expires: Wed, 21 Oct 2015 07:28:00 GMT
...binary....data...chunk1..myvideo..binary....data....chunk2..content...binary....data....chunk3..and...binary......data......chunk4...etc...
For more information: Chunked transfer encoding — Wikipedia, Trailer - HTTP | MDN
Related
We are trying to send and encrypted EDI(Electronic Data Interchange) String along with a digital signature in the form of SMIME to Canada Border Services Agency ,but getting Error everytime we try different approaches.
The request SMIME payload we are sending is :
Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha1;
boundary="----=_Part_0_1964847681.1577187780616"
------=_Part_0_1964847681.1577187780616
Content-Type: application/edi-edifact
Content-Length: 881
UNA:+.?'
UNB+UNOA:3+KAGATEWAY:CBSANETWORKID+191017:0930+REF12345'UNG+CUSDEC+ +IIDT+191017:0930+GREF12345+UN+D:13A:IID'
UNH+MREF12345+GOVCBR:D:13A:UN:IID'
BGM+929+119081234567X+9'
DTM+132:CCYYMMDDHHMMZZZ:303'MOA+134:10000:CAD'
RFF+CN:WXYZ12345678987654321'
GOR++5'LOC+23+0809+3470'
NAD+IM+868929415RM0001++ INC+6955 AVE+NEWARK+CA+94560+US'UNS+D'
SEQ+1'NAD+VN+++ZOY HOME FURNISHING CO+BUILDING 2-5:TANGPU INDUSTRIAL PARK+DIPU,ANJI+ZJ+313301+CN'
NAD+UC+868929415RM0001++ CAN+123 +VANCOUVERBC+V5K1A5+CA'LOC+35+CN'
DTM+757:20191001:102'DOC+380+98027209'SEQ+1'DTM+3:20190901:102'MOA+39:10000CAD'MEA+AAE+AAB+KGM:5000'QTY+47:180:H87'LIN+1'NAD+MF+++ZOY HOME CO+BUILDING 2-5: INDUSTRIAL PARK+DIPU,ANJI+ZJ+313301+CN'
LOC+27+CN'PAC+180+3+BX'SEQ+1'GID+1'
IMD++8+:::METAL +AAE+ABS+KGM:4500'MOA+66:10000CAD'TCC+++9401791000:HS'CNT+51:180:EA'SEQ+1'HYN+3'UNS+S'UNT+20+MREF12345'UNE+1+GREF12345'UNZ+1+REF12345'
------=_Part_0_1964847681.1577187780616
Content-Type: application/pkcs7-signature
Content-Transfer-Encoding: base64
Content-Length: 1640
0�� *�H��
���0��1�u0�q0Y0Q1
0 UCA1
0 U
GC10U
cra-arc10
U
Extern10U
EXTTESTCASd=0
*�H��
� {/d�R'^J��o�����.�M5Q:��^��Ў�2�<�)9a �VS�|O�.�P/�(N�"�IJ��0_HH�m!��I.
j3�a �`����g�4|ll.�M��uVW�8��2�y[�$0_���j
���m�2���uul��9ehqS��N�O����������{m'� >�ϻ��cL���9�R�g�͜����{�O
�h-�UJ��(��W�dԼd\
Q7��|��:����P����0� *�H��
0 *�H��}B
0_.h�U�������"�q�����S �C2X������*a�
�eٲQ��>`˨�?�����|i���a��³ev�
��������`�����cY]�����#�Ӡ(n�o��Jm���!�F?J�1� �6]M�l#]��u֡p�;�Fn����
����w8Dz�Q�
����R�����~h�K��E��
�n{ϬA��3LA=����y�!��?7�h`\[%�y>�%10������� s�-��z=E�,�K�������:��9�?�F��
VpT#A�7�[=�� �y2�w�o:[�"�;���ŗp5�C�uk��qf5l�
Ȑ���:��X��Y1�ς�ˆ?P�]���^�����8�m�:3|���#Qm=0\���r �����X�
q�Eڂz�A�a\1l�D�4,x�V�g79c��)>T�ժ���d:��]<�.ri�#vl����C������|T��l��<;�n�,�H�p~D����;��VN�VT�*�o:���y%�jfh4�S���Pf)>Z����5��L�<�
R���������#�3Ҫ��T�a�nMh6q#cێ���eʯ�p$ƅv����� cb�zQc�&�5li���zg!��
E�)�i�$v��%Y'�
o�zX����q���P�)F��c<�`e�B~
&��SL�J��M��������o�M�K�Q{��<m�|��"�yo��(�%�_���g�����$���NiV�z�u����u��T��_#g���b��r�H��̨�'rw���:n[!�e#
o_�� �l2'2�
�T�Q��7�k7�(�4�e���G"����>{'1b�4,?Y���#)
��u(�ч���ܤ6weMLY��b�f�����8}&��v��?�Ti�R:g$�P5��v�Ž~���
܅��q��RK;��
*"N�G�O�[R����>��Cm8o懏�e�T_
------=_Part_0_1964847681.1577187780616--
The exception is:
Response code : 400
Response Message : Bad Request
Error Message : 400 BAD REQUESTYour transaction <<393BDE.000003.00179#yourhost.yourcompany.com>> could not be processed due to the following error(s):
java.io.IOException: Unexpected response code received from servlet: 400
EERROR 2207:Smime does not conains 2 body parts
END: Communication with servlet
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)--
If anyone has worked with CBSA or worked with entrust or EDI ..please help we are stuck
Any help related to the exception in SMIME will also be really helpful
The code I m using is:
mbp1.addHeader("Content-Length", String.valueOf(testMessage.length()));
PropUtil.getBooleanSystemProperty("mail.mime.multipart.ignoreexistingboundaryparameter", true);
// contentType = new ContentType("multipart","mixed",pml);
// contentType.setParameter("boundary", getBoundary());
//
// contentType1 = new ContentType("multipart","mixed",pml1);
// contentType1.setParameter("boundary", getBoundary());
//
// contentType2 = new ContentType("multipart","mixed",pml2);
// contentType2.setParameter("boundary", getBoundary());
// contentType = type.toString();
MimeMultipart mimeMultipart = new MyMimEMultipart("signed");
SMimeBodyPart ediEdiPart = new SMimeBodyPart();
ByteArrayOutputStream fout = new ByteArrayOutputStream();
fout.write(testMessage.getBytes());
ByteArrayOutputStream encoded = (ByteArrayOutputStream)MimeUtility.encode(fout, "7bit");
byte[] gg = encoded.toByteArray();
InternetHeaders internetHeaders = new InternetHeaders();
internetHeaders.addHeader("Content-Type", "application/edi-edifact");
internetHeaders.addHeader("Content-Length", String.valueOf(gg.length));
internetHeaders.addHeader("Content-Transfer-Encoding","7-bit");
SMimeBodyPart newthing= new SMimeBodyPart(internetHeaders,gg);
ByteArrayOutputStream consoleStream = new ByteArrayOutputStream();
newthing.writeTo(consoleStream);
// consoleStream.writeTo(System.out);
FileOutputStream f= new FileOutputStream("/Users/lakshyakumarsingh/Documents/kx-github/kn-cbsaproxy/src/main/resources/test1_decode.txt");
f.write(consoleStream.toByteArray());
// System.out.println("this is the input");
// System.out.println(consoleStream.toString("US-ASCII"));
// ediEdiPart.setHeader("Message-ID","29123185.1.1449072497221.JavaMail.axg129#WH12CU10178A");
// ediEdiPart.setHeader("Content-Type", "application/edi-edifact");
// int contentlength = testMessage.length();
ediEdiPart.setDataHandler(dataHandler);
// ediEdiPart.setText(finalString);
// ediEdiPart.setHeader("Content-Transfer-Encoding", "binary");
ediEdiPart.setHeader("Content-Length", String.valueOf(testMessage.length()));
// ediEdiPart.setContent(testMessage, "application/edi-edifact");
// mimeMultipart.setParent(PART.);
// System.out.println("contetnt type is : " + contentType.toString());
mimeMultipart.setSubType("signed");
mimeMultipart.setPreamble(
"Content-Type: multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha1; \n" +
"\tboundary=\"----=_Part_0_1964847681.1577187780616\"");
mimeMultipart.addBodyPart(newthing);
SMimeBodyPart mbp2 = new SMimeBodyPart();
mbp2.addHeader("Content-Type", "application/pkcs7-signature");
mbp2.addHeader("Content-Transfer-Encoding", "binary");
// mbp2.addHeader("Content-Description", "S/MIME Cryptographic Signature");
I am trying to use wrk for https request generation. How do I pass multipart/form-data request to wrk.format function. My request looks like
Accept: application/json
Content-Type: multipart/form-data; boundary="----=_Part_0_140715475.1525373091109"
MIME-Version: 1.0
------=_Part_0_140715475.1525373091109
Content-Type: application/json; name=PersonName.json
Content-Transfer-Encoding: binary
Content-Disposition: form-data; name="PersonName"; filename="PersonName.json"
{
"Person":{
"Name":"test2",
"age":"23"
}
}
------=_Part_0_140715475.1525373091109--
below wrk_script in lua is not working if I read above request block as jsonBody variable in code and pass as body argument to wrk.format function.
-- Load data from the file
function load_data_from_file(file)
lines = {}
-- Check if the file exists
local f=io.open(file,"r")
if f~=nil then
io.close(f)
else
-- Return the empty array
return lines
end
-- If the file exists loop through all its lines
-- and add them into the lines array
for line in io.lines(file) do
if not (line == '') then
for a, b in line:gmatch( "([^,]+),([^,]+)" ) do
lines[#lines + 1] = { person = a, jsonBody = b }
end
end
end
return lines
end
function init(args)
requests = 0
responses = 0
wrk.method = '_REQUESTMETHOD_'
wrk.body = '_REQUESTBODY_'
_HEAD_
end
-- Load data from file, here testdata.csv, should be in this directory
dataSets = load_data_from_file("testdata.csv")
if #dataSets == 0 then
print("Not a valid input data file. [testdata.csv]")
return
end
-- Initialize the dataSets array iterator
counter = 0
-- request handler
request = function()
counter = counter + 1
local req = nil
if dataSets[counter] ~= nil then
-- Get the next dataSets array element
url_path = "/person/" .. dataSets[counter].person .. "/names"
req = wrk.format(nil, url_path, nil, dataSets[counter].jsonBody )
end
-- If the counter is longer than the dataSets array length then reset it
if counter > #dataSets then
counter = 0
end
-- Return the request object with the current URL path
return req
end
-- response handler
response = function(status, headers, body)
end
looking for experts advice
I'm performing a request to a foreign API using luasec, lua-socket and converting the data, a JSON string, to a lua table with cjson. I've read the docs of said modules and unfortunately none of it helped me with my problem. Can't link more than 2 websites with current account, sorry.
Summary: I get the response and the appropiate string using the posted request function, when turning said string into a lua table via cjson.decode the output table isn't the desired one, it's a copy of my response header, which is not intentional.
The following code is how I do my request:
local function request (req_t)
local res_t = {}
resp = https.request {
url = const.API_URL .. req_t.url,
method = req_t.method,
headers = req_t.headers,
sink = ltn12.sink.table(res_t)
}
return table.concat(res_t), resp.headers, resp.code
end
Using the following call
local res, headers = request({ ... })
I receive the proper response as a string but my goal is to do data manipulation with it, so turning said response(string) to a lua table with
local resJson = cjson.decode(res)
Does not produce the correct output. It does produce a table which is exactly the same as my response header. Here is the following output from my terminal alongside the code
When out of function type is: string
Desired response in string:
{"total_photos":221926,"photo_downloads":"186029632.0"}
When out of function type is: string
Desired response in string:
{"total_photos":221926,"photo_downloads":"186029632.0"}
After decode, type is: table
server Cowboy
strict-transport-security max-age=31536000
access-control-allow-headers *
x-ratelimit-limit 50
x-ratelimit-remaining 46
x-cache-hits 0, 0
accept-ranges bytes
access-control-request-method *
x-request-id ee5a74fd-2b10-4f46-9c25-5cfc53aeac6c
access-control-expose-headers Link,X-Total,X-Per-Page,X-RateLimit-Limit,X-RateLimit-Remaining
content-type application/json
connection close
content-length 55
fastly-debug-digest f62d52c08b1ef74db89a66a0069f0a35c49e52230567905240dacf08c9ea1813
vary Origin
cache-control no-cache, no-store, must-revalidate
x-timer S1496524765.369880,VS0,VE111
x-cache MISS, MISS
x-served-by cache-iad2123-IAD, cache-mad9429-MAD
via 1.1 vegur, 1.1 varnish, 1.1 varnish
date Sat, 03 Jun 2017 21:19:25 GMT
age 0
access-control-allow-origin *
x-runtime 0.011667
Printing header
server Cowboy
strict-transport-security max-age=31536000
access-control-allow-headers *
x-ratelimit-limit 50
x-ratelimit-remaining 46
x-cache-hits 0, 0
accept-ranges bytes
access-control-request-method *
x-request-id ee5a74fd-2b10-4f46-9c25-5cfc53aeac6c
access-control-expose-headers Link,X-Total,X-Per-Page,X-RateLimit-Limit,X-RateLimit-Remaining
content-type application/json
connection close
content-length 55
fastly-debug-digest f62d52c08b1ef74db89a66a0069f0a35c49e52230567905240dacf08c9ea1813
vary Origin
cache-control no-cache, no-store, must-revalidate
x-timer S1496524765.369880,VS0,VE111
x-cache MISS, MISS
x-served-by cache-iad2123-IAD, cache-mad9429-MAD
via 1.1 vegur, 1.1 varnish, 1.1 varnish
date Sat, 03 Jun 2017 21:19:25 GMT
age 0
access-control-allow-origin *
x-runtime 0.011667
Function that produces said log
local res, headers = request({ ... })
print('When out of function type is: ' ..type(res) .. '\n')
print('Desired response in string:')
print(res .. '\n')
resJson = cjson.decode(res)
print('\nAfter decode, type is: ' .. type(resJson) .. '\n')
pTable(resJson)
print('\nPrinting header\n')
pTable(headers)
pTable is just a function to output a table to stdout.
Thanks in advance
Posted function and routines are correct. The problem was located in my print table function, which I somehow had hardcoded my headers.
We use AS3 Event:ProcessEvent.SOCKET_DATA to listen for socket data.
So this is my AS3 code for socket data handle.
private function packetHandler( e:ProgressEvent ):void
{
while( m_socket.bytesAvailable && m_socket.bytesAvailable >= pLen )
{
//pLen means the packet length
//pLen init is zero
if( pLen == 0 )
{
//PACKET_LEN stands for the solid length of one packet
//PACKET_LEN = HEAD_LEN + 4
//the 4 means an unsigned int which means the packet content length
if( m_socket.bytesAvailable > PACKET_LEN )
{
m_socket.readBytes( headByteBuffer, 0, HEAD_LEN );
headByteBuffer.clear();
pLen = m_socket.readUnsignedInt() + 4;
}
else
{
break;
}
}
//recieved a whole packet now handle it
else
{
var newPacket:ByteArray = new ByteArray();
newPacket.endian = Endian.LITTLE_ENDIAN;
m_socket.readBytes( newPacket, 0, pLen );
parasMsg( newPacket, pLen-4 );
pLen = 0;
}
}
}
A whole packet can be described in this picture:
My Problem is: When there has one incomplete packet received in Flash and triggered the handle.
But the left part of the packet will never trigger the handle and it seems like that the left part of the packet has lost!!!
I used a capture tool, find that the tcp packet is ok, but why the left part doesn't trigger the event again?
You can get more debug information below. Thank you!
This is my log:
byteava means bytesAvailable of m_socket
==>sendPacket: {"rangeID":"1","uid":"145962","serviceType":"copyscene","cmd":"CopySceneMoveAsk","pathPoint":[{"col":7,"row":6},{"col":7,"row":5},{"col":7,"row":4},{"col":7,"row":3},{"col":6,"row":3}],"sn":"79","smallPathPoint":[[22,19],[22,18],[22,17],[22,16],[22,15],[22,14],[22,13],[21,13],[21,12],[21,11],[20,11],[20,10]]}, bytesLoaded = 463
ProgressEvent Triggered!0 socket byteava = 373 evt loaded:373 evt total:0 evt:[ProgressEvent type="socketData" bubbles=false cancelable=false eventPhase=2 bytesLoaded=373 bytesTotal=0]
Find a packet from socket, pLen=288 socket byteava = 276
ProgressEvent Triggered!288 socket byteava = 441 evt loaded:165 evt total:0 evt:[ProgressEvent type="socketData" bubbles=false cancelable=false eventPhase=2 bytesLoaded=165 bytesTotal=0]
Start to Read a packet to buffer, pLen=288 socket byteava = 441
whole packet content: Readed a packet to buffer, pLen=288 socket byteava = 153
Server packet content byte buffer ava:288 len:288 pos: 0
Server Paras Data : data len: 284 data content: {"cmd":"CopySceneMoveNotify","gtcmd":"108","layer":"1","pathPoint":[{"col":7,"row":6},{"col":7,"row":5},{"col":7,"row":4},{"col":7,"row":3},{"col":6,"row":3}],"smallPathPoint":[[22,19],[22,18],[22,17],[22,16],[22,15],[22,14],[22,13],[21,13],[21,12],[21,11],[20,11],[20,10]HTTP/1.1 200
_[20,10]HTTP/1.1 200_ This is what went wrong!! The incomplete packet cat with another packet's header.
Here is the capture of the TCP connections:
Hope you can vote it up so that I can put my pictures of this question on!
My English is not very good, hope you can understand what I mean.
Thank you!
The Socket's event flash.events.ProgressEvent.SOCKET_DATA will fire when you receive the data at this point you can get the received bytes ( check .bytesAvailable ). When the msg is split into multiple packages you will receive event for each packet.
In your case maybe the pLen have wrong value when check m_socket.bytesAvailable >= pLen.
I assume you send the msg size in the begging of the message ( in this case you can check if the whole msg is received ). In this case you must have a class member (ByteArray ) as buffer that holds a received bytes. When new data come you must copy the new bytes to this member and than check if you receive whole msg. If buffer contains whole msg than remove the msg from it.
In general your event handler must looks like this:
protected function onSocketData( pEvt: Event ): void
{
try
{
if ( 0 < pEvt.target.bytesAvailable )
{
var byteStream: ByteArray = new ByteArray();
pEvt.target.readBytes( byteStream, 0, Socket( pEvt.target ).bytesAvailable );
// Append readed data to your buffer
do
{
//Check if you have enough bytes to read whole msg and execute it
//do..while because maybe it can be more than one msg in buffer
}
while ( null != msgContent );
}
}
catch ( exc )
{
}
}
Problem should caused by the packet's solid header.
Below is the 93 bytes solid header of a packet.
private static const HTTP_RESPONSE_CONTENT : String = "HTTP/1.1 200 OK \r\n"
+ "Connection: keep-alive \r\n"
+ "Content-Length: 280 \r\n"
+ "Content-Type: text/html \r\n\r\n";
This header will be in every packet's header, which AS3 could treat it to a http and might cut the flow with Content-Length: 280. So the left part of the 280 bytes will never trigger the SOCKET_DATA event.
When I remove this header, it's ok now.
I'm trying to set up a websocket server in node.js but having problems. I found a bit code here on stackoverflow and heres the servercode I have now:
var net = require("net"), crypto = require("crypto"), users = [];
net.createServer(function(socket) {
this.name = "Anonymous";
users.push(socket);
socket.on('data', function(buffer) {
if(buffer.toString('utf-8').substring(0, 14) === "GET / HTTP/1.1") {
this.securyPattern = /Sec-WebSocket-Key: (.*)/g;
this.key = this.securyPattern.exec(buffer);
this.magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
this.sha1 = crypto.createHash("sha1");
this.sha1.update(this.key[1] + this.magic);
this.accept = this.sha1.digest("base64");
socket.write("HTTP/1.1 101 Switching Protocols\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + this.accept + "\r\n\r\n");
} else {
console.log(buffer);
console.log(buffer.toString('utf-8'));
}
});
socket.on('end', function() {
users.splice(users.indexOf(socket), 1);
});
}).listen(1337);
Everything works fine as it connects, and users.length is updated when that happens and when someone disconnects.
The problem is that I dont know how to read messages except the header (which is plain text), so the lines that I have to print the buffer and buffer.toString('utf-8') only prints something binary different all the time, example for the word "hello":
<Buffer 81 85 71 dc c1 02 19 b9 ad 6e 1e>
??q??☻↓??n▲
<Buffer 81 85 8e 8f 0f a2 e6 ea 63 ce e1>
????☼???c??
I'm sending this "hello" with Chrome 16 using:
myWebSocket.send("hello"); where myWebSocket is the WebSocket object.
So how do I read and write messages to the socket with this?
Note that after the handshake, the data is framed with 2 or more header bytes at the beginning of each frame. Also, note that payload sent from the client (browser) to the server is masked using a simple 4-byte running XOR mask.
The framing definition is defined in section 5 of the spec
Instead of implementing your own WebSocket server in Node you might consider using a higher level abstraction like Socket.IO.
Here's my code on handling that buffer:
socket.ondata = function(src,start,end) {
src = src.slice(start,end);
var maskKeys = [src[2],src[3],src[4],src[5]];
var dest = new Array();
for(var i=0;i<src.length-6;i++){
var mKey = maskKeys[i%4];
dest[i] = mKey ^ src[6+i];
}
console.log(new Buffer(dest).toString());
}
Found from here: http://songpengfei.iteye.com/blog/1178310