I am trying to determine the best way to accomplish the following task and could use some help. I am writing a web-based (using hunchentoot, of course) application for managing voicemail that is stored in Cisco's Unity system. I can access the voicemail box of a given use via IMAP. The mel project has helped make that part easy. However, I am not sure the best way to send the audio stream to the user's browser. The method below illustrates (inefficiently) what I need to do. Can anyone suggest a more efficient method that does not use (send-headers)? The hunchentoot docs say, "If your handlers return the full body as a string or as an array of octets, you should not call this function." However, I cannot find an alternative way to do what I need to do. I must fetch the voicemail as an email via IMAP and then decode the base64-encoded body, which contains the voicemail as a wav file. Writing out a wav file and pointing the user's browser to it would introduce security issues, so I must do this entire process in memory. Any help is appreciated.
(defmethod send-audio-stream ((user user) msg-id) "Convert the message body and send the wav content to the user." (handler-case (let ((msg (mel:find-message (mbox user) msg-id))) (setf (content-type) "audio/x-wav") (setf (header-out "Content-Disposition") (format t "inline; filename=~a.wav" msg-id)) (setf (content-length) ;; FIXME! Why do this twice? (length (base64-decode-msg-body-to-array msg))) (let ((stream (send-headers))) (base64-decode-msg-body-to-stream msg :stream stream))) (error (condition) (format nil "Unable to play message: ~a" condition))))
Cheers. Kevin Raison
Kevin,
why do you feel that you need to set the content length explictly? By default, SEND-HEADERS returns a chunked stream, so setting the content length is not required. My suggestion is to keep using SEND-HEADERS and let the base64 decoder write to the stream. There is nothing wrong with your approach, except for the content length setting, which seems wasteful.
-Hans
On Tue, Aug 5, 2008 at 22:25, Kevin Raison raison@chatsubo.net wrote:
I am trying to determine the best way to accomplish the following task and could use some help. I am writing a web-based (using hunchentoot, of course) application for managing voicemail that is stored in Cisco's Unity system. I can access the voicemail box of a given use via IMAP. The mel project has helped make that part easy. However, I am not sure the best way to send the audio stream to the user's browser. The method below illustrates (inefficiently) what I need to do. Can anyone suggest a more efficient method that does not use (send-headers)? The hunchentoot docs say, "If your handlers return the full body as a string or as an array of octets, you should not call this function." However, I cannot find an alternative way to do what I need to do. I must fetch the voicemail as an email via IMAP and then decode the base64-encoded body, which contains the voicemail as a wav file. Writing out a wav file and pointing the user's browser to it would introduce security issues, so I must do this entire process in memory. Any help is appreciated.
(defmethod send-audio-stream ((user user) msg-id) "Convert the message body and send the wav content to the user." (handler-case (let ((msg (mel:find-message (mbox user) msg-id))) (setf (content-type) "audio/x-wav") (setf (header-out "Content-Disposition") (format t "inline; filename=~a.wav" msg-id)) (setf (content-length) ;; FIXME! Why do this twice? (length (base64-decode-msg-body-to-array msg))) (let ((stream (send-headers))) (base64-decode-msg-body-to-stream msg :stream stream))) (error (condition) (format nil "Unable to play message: ~a" condition))))
Cheers. Kevin Raison _______________________________________________ tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
Thanks for the response, Hans. To explain, I added the explicit content length header after seeing some strange and inconsistent behavior with browsers across several platforms. On the Mac using Firefox, the entire WAV stream would not reach the browser, so users would only hear part of their audio file. With Safari, the stream would not play at all. On Windows, IE behaved like Firefox on the Mac, but Firefox on Windows would not play the WAV stream at all. On Linux using Firefox, the browser would generally crash mid-way through the stream downloading. Opera on Linux worked fine. Explicitly adding the content length header solved all of these issues. Any ideas?
Thanks, Kevin
Hans Hübner wrote:
Kevin,
why do you feel that you need to set the content length explictly? By default, SEND-HEADERS returns a chunked stream, so setting the content length is not required. My suggestion is to keep using SEND-HEADERS and let the base64 decoder write to the stream. There is nothing wrong with your approach, except for the content length setting, which seems wasteful.
-Hans
On Tue, Aug 5, 2008 at 22:25, Kevin Raison raison@chatsubo.net wrote:
I am trying to determine the best way to accomplish the following task and could use some help. I am writing a web-based (using hunchentoot, of course) application for managing voicemail that is stored in Cisco's Unity system. I can access the voicemail box of a given use via IMAP. The mel project has helped make that part easy. However, I am not sure the best way to send the audio stream to the user's browser. The method below illustrates (inefficiently) what I need to do. Can anyone suggest a more efficient method that does not use (send-headers)? The hunchentoot docs say, "If your handlers return the full body as a string or as an array of octets, you should not call this function." However, I cannot find an alternative way to do what I need to do. I must fetch the voicemail as an email via IMAP and then decode the base64-encoded body, which contains the voicemail as a wav file. Writing out a wav file and pointing the user's browser to it would introduce security issues, so I must do this entire process in memory. Any help is appreciated.
(defmethod send-audio-stream ((user user) msg-id) "Convert the message body and send the wav content to the user." (handler-case (let ((msg (mel:find-message (mbox user) msg-id))) (setf (content-type) "audio/x-wav") (setf (header-out "Content-Disposition") (format t "inline; filename=~a.wav" msg-id)) (setf (content-length) ;; FIXME! Why do this twice? (length (base64-decode-msg-body-to-array msg))) (let ((stream (send-headers))) (base64-decode-msg-body-to-stream msg :stream stream))) (error (condition) (format nil "Unable to play message: ~a" condition))))
Cheers. Kevin Raison _______________________________________________ tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
Actually, let me add a correction to my last post (too many browsers, hard to remember which one did what):
On Windows, without setting the content length header explicitly, IE would play the WAV stream properly using iTunes as its helper application, while Firefox would only play part of the stream using Quicktime as its helper.
Cheers. Kevin
Kevin Raison wrote:
Thanks for the response, Hans. To explain, I added the explicit content length header after seeing some strange and inconsistent behavior with browsers across several platforms. On the Mac using Firefox, the entire WAV stream would not reach the browser, so users would only hear part of their audio file. With Safari, the stream would not play at all. On Windows, IE behaved like Firefox on the Mac, but Firefox on Windows would not play the WAV stream at all. On Linux using Firefox, the browser would generally crash mid-way through the stream downloading. Opera on Linux worked fine. Explicitly adding the content length header solved all of these issues. Any ideas?
Thanks, Kevin
Hans Hübner wrote:
Kevin,
why do you feel that you need to set the content length explictly? By default, SEND-HEADERS returns a chunked stream, so setting the content length is not required. My suggestion is to keep using SEND-HEADERS and let the base64 decoder write to the stream. There is nothing wrong with your approach, except for the content length setting, which seems wasteful.
-Hans
On Tue, Aug 5, 2008 at 22:25, Kevin Raison raison@chatsubo.net wrote:
I am trying to determine the best way to accomplish the following task and could use some help. I am writing a web-based (using hunchentoot, of course) application for managing voicemail that is stored in Cisco's Unity system. I can access the voicemail box of a given use via IMAP. The mel project has helped make that part easy. However, I am not sure the best way to send the audio stream to the user's browser. The method below illustrates (inefficiently) what I need to do. Can anyone suggest a more efficient method that does not use (send-headers)? The hunchentoot docs say, "If your handlers return the full body as a string or as an array of octets, you should not call this function." However, I cannot find an alternative way to do what I need to do. I must fetch the voicemail as an email via IMAP and then decode the base64-encoded body, which contains the voicemail as a wav file. Writing out a wav file and pointing the user's browser to it would introduce security issues, so I must do this entire process in memory. Any help is appreciated.
(defmethod send-audio-stream ((user user) msg-id) "Convert the message body and send the wav content to the user." (handler-case (let ((msg (mel:find-message (mbox user) msg-id))) (setf (content-type) "audio/x-wav") (setf (header-out "Content-Disposition") (format t "inline; filename=~a.wav" msg-id)) (setf (content-length) ;; FIXME! Why do this twice? (length (base64-decode-msg-body-to-array msg))) (let ((stream (send-headers))) (base64-decode-msg-body-to-stream msg :stream stream))) (error (condition) (format nil "Unable to play message: ~a" condition))))
Cheers. Kevin Raison _______________________________________________ tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
A cheap workaround would be to use HTTP/1.0 (no persistent connection, non Content-Length header required). I lack the time to try this out myself, but what you write seems like being indicative of a problem in the chunked encoding.
If you can afford the main memory footage of the WAV data encoded as octets, you could also just return the vector from your handler and let Hunchentoot deal with setting the headers properly.
-Hans
On Wed, Aug 6, 2008 at 11:27, Kevin Raison raison@chatsubo.net wrote:
Actually, let me add a correction to my last post (too many browsers, hard to remember which one did what):
On Windows, without setting the content length header explicitly, IE would play the WAV stream properly using iTunes as its helper application, while Firefox would only play part of the stream using Quicktime as its helper.
Cheers. Kevin
Kevin Raison wrote:
Thanks for the response, Hans. To explain, I added the explicit content length header after seeing some strange and inconsistent behavior with browsers across several platforms. On the Mac using Firefox, the entire WAV stream would not reach the browser, so users would only hear part of their audio file. With Safari, the stream would not play at all. On Windows, IE behaved like Firefox on the Mac, but Firefox on Windows would not play the WAV stream at all. On Linux using Firefox, the browser would generally crash mid-way through the stream downloading. Opera on Linux worked fine. Explicitly adding the content length header solved all of these issues. Any ideas?
Thanks, Kevin
Hans Hübner wrote:
Kevin,
why do you feel that you need to set the content length explictly? By default, SEND-HEADERS returns a chunked stream, so setting the content length is not required. My suggestion is to keep using SEND-HEADERS and let the base64 decoder write to the stream. There is nothing wrong with your approach, except for the content length setting, which seems wasteful.
-Hans
On Tue, Aug 5, 2008 at 22:25, Kevin Raison raison@chatsubo.net wrote:
I am trying to determine the best way to accomplish the following task and could use some help. I am writing a web-based (using hunchentoot, of course) application for managing voicemail that is stored in Cisco's Unity system. I can access the voicemail box of a given use via IMAP. The mel project has helped make that part easy. However, I am not sure the best way to send the audio stream to the user's browser. The method below illustrates (inefficiently) what I need to do. Can anyone suggest a more efficient method that does not use (send-headers)? The hunchentoot docs say, "If your handlers return the full body as a string or as an array of octets, you should not call this function." However, I cannot find an alternative way to do what I need to do. I must fetch the voicemail as an email via IMAP and then decode the base64-encoded body, which contains the voicemail as a wav file. Writing out a wav file and pointing the user's browser to it would introduce security issues, so I must do this entire process in memory. Any help is appreciated.
(defmethod send-audio-stream ((user user) msg-id) "Convert the message body and send the wav content to the user." (handler-case (let ((msg (mel:find-message (mbox user) msg-id))) (setf (content-type) "audio/x-wav") (setf (header-out "Content-Disposition") (format t "inline; filename=~a.wav" msg-id)) (setf (content-length) ;; FIXME! Why do this twice? (length (base64-decode-msg-body-to-array msg))) (let ((stream (send-headers))) (base64-decode-msg-body-to-stream msg :stream stream))) (error (condition) (format nil "Unable to play message: ~a" condition))))
Cheers. Kevin Raison _______________________________________________ tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
On Wed, 06 Aug 2008 09:16:29 -0700, Kevin Raison raison@chatsubo.net wrote:
To explain, I added the explicit content length header after seeing some strange and inconsistent behavior with browsers across several platforms. On the Mac using Firefox, the entire WAV stream would not reach the browser, so users would only hear part of their audio file. With Safari, the stream would not play at all. On Windows, IE behaved like Firefox on the Mac, but Firefox on Windows would not play the WAV stream at all. On Linux using Firefox, the browser would generally crash mid-way through the stream downloading. Opera on Linux worked fine. Explicitly adding the content length header solved all of these issues. Any ideas?
Which version of Hunchentoot are you using, dev or release? Have you checked that it actually does the right thing when sending chunked content? I would hope that it does, but with the dev version I'm less sure and even with the release version there's of course always a chance that there are bugs. Also, could it be that you get requests for partial content somewhere that aren't handled as expected?
Edi (on vacation).
Thanks for the response, Edi. I am using 0.15.7. I tried Hans' recommendation of converting the WAV data into octets and then having the handler return those octets. This seems to have fixed the issue.
Further analysis of the Safari-related problems revealed something else: it seems that when Safari calls its helper application (in this case, Quicktime), it hands the helper a URL and all relevant cookies. Quicktime then does a GET which does not succeed because it is sending the wrong User Agent string for its Hunchentoot session cookie. <sigh> I guess I'll switch to IP-based sessions.
Thanks to both of you for your help.
Cheers. Kevin
Edi Weitz wrote:
On Wed, 06 Aug 2008 09:16:29 -0700, Kevin Raison raison@chatsubo.net wrote:
To explain, I added the explicit content length header after seeing some strange and inconsistent behavior with browsers across several platforms. On the Mac using Firefox, the entire WAV stream would not reach the browser, so users would only hear part of their audio file. With Safari, the stream would not play at all. On Windows, IE behaved like Firefox on the Mac, but Firefox on Windows would not play the WAV stream at all. On Linux using Firefox, the browser would generally crash mid-way through the stream downloading. Opera on Linux worked fine. Explicitly adding the content length header solved all of these issues. Any ideas?
Which version of Hunchentoot are you using, dev or release? Have you checked that it actually does the right thing when sending chunked content? I would hope that it does, but with the dev version I'm less sure and even with the release version there's of course always a chance that there are bugs. Also, could it be that you get requests for partial content somewhere that aren't handled as expected?
Edi (on vacation). _______________________________________________ tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
On Wed, 06 Aug 2008 11:15:08 -0700, Kevin Raison raison@chatsubo.net wrote:
Further analysis of the Safari-related problems revealed something else: it seems that when Safari calls its helper application (in this case, Quicktime), it hands the helper a URL and all relevant cookies. Quicktime then does a GET which does not succeed because it is sending the wrong User Agent string for its Hunchentoot session cookie. <sigh> I guess I'll switch to IP-based sessions.
Set this one to NIL:
http://weitz.de/hunchentoot/#*use-user-agent-for-sessions*
Maybe you can try this and report back to the list if this solves your problems? I'd be happy to hear that you didn't find bugs related to chunked encoding... :)
Edi (still on vacation).
Edi, this solved the issues with Safari and a few other odd browser setups. You can rest assured that I did not discover a chunked encoding bug!
Thanks again for the help. Kevin
Edi Weitz wrote:
On Wed, 06 Aug 2008 11:15:08 -0700, Kevin Raison raison@chatsubo.net wrote:
Further analysis of the Safari-related problems revealed something else: it seems that when Safari calls its helper application (in this case, Quicktime), it hands the helper a URL and all relevant cookies. Quicktime then does a GET which does not succeed because it is sending the wrong User Agent string for its Hunchentoot session cookie. <sigh> I guess I'll switch to IP-based sessions.
Set this one to NIL:
http://weitz.de/hunchentoot/#*use-user-agent-for-sessions*
Maybe you can try this and report back to the list if this solves your problems? I'd be happy to hear that you didn't find bugs related to chunked encoding... :)
Edi (still on vacation). _______________________________________________ tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel