7

I'm trying to use haxr 3000.8.5 to upload images to a WordPress blog using the metaWeblog API---specifically, the newMediaObject method.

I've gotten it to work for small images, having successfully uploaded 20x20 icons in both PNG and JPG formats. However, when I try medium-sized images (say, 300x300) I get an ErrorClosed exception, presumably from the HTTP package (I did a bit of source diving, and found that haxr ultimately calls Network.HTTP.simpleHTTP).

Can anyone shed light on the reasons why a call to simpleHTTP might fail with ErrorClosed? Suggestions of things to try and potential workarounds are also welcome.

Here are links to full tcpdump output from a successful upload and from an unsuccessful upload.

The (sanitized) code is also shown below, in case it's of any use.

import           Network.XmlRpc.Client      (remote)
import           Network.XmlRpc.Internals   (Value(..), toValue)
import           Data.Char                  (toLower)
import           System.FilePath            (takeFileName, takeExtension)
import qualified Data.ByteString.Char8 as B
import           Data.Functor               ((<$>))

uploadMediaObject :: FilePath -> IO Value
uploadMediaObject file = do
  media <- mkMediaObject file
  remote "http://someblog.wordpress.com/xmlrpc.php" "metaWeblog.newMediaObject"
    "default" "username" "password" media

-- Create the required struct representing the image.
mkMediaObject :: FilePath -> IO Value
mkMediaObject filePath = do
  bits <- B.unpack <$> B.readFile filePath
  return $ ValueStruct
    [ ("name", toValue fileName)
    , ("type", toValue fileType)
    , ("bits", ValueBase64 bits)
    ]
  where
    fileName = takeFileName filePath
    fileType = case (map toLower . drop 1 . takeExtension) fileName of
                 "png"  -> "image/png"
                 "jpg"  -> "image/jpeg"
                 "jpeg" -> "image/jpeg"
                 "gif"  -> "image/gif"

main = do
  v <- uploadMediaObject "images/puppy.png"
  print v
Brent Yorgey
  • 2,043
  • 16
  • 17
  • 1
    Have you tried using tcpdump or wireshark to inspect the HTTP session? – Nathan Howell Jun 30 '12 at 21:51
  • Thanks for the suggestion. I really know very little about networking, especially when it comes to debugging. I've added some tcpdump output to the question. – Brent Yorgey Jun 30 '12 at 22:01
  • @BrentYorgey: I think you'll want to edit that output into your question. It's much easier to read it properly formatted there, even if it manages to fit into the limited space you get on a comment. – Tikhon Jelvis Jun 30 '12 at 22:05
  • Yep, sorry, hit Enter too soon. – Brent Yorgey Jun 30 '12 at 22:07
  • Is there a VPN tunnel or so in play? When smaller uploads work and larger not this is often pointing to issues with packet fragmentation and reassembly failing. – J Fritsch Jun 30 '12 at 22:18
  • No VPN tunnels or anything out of the ordinary (that I know of); I am just on a local wireless network, with the wireless router connected to a WiMAX modem which connects to my ISP. – Brent Yorgey Jun 30 '12 at 22:20
  • 1
    The default output doesn't have enough info to be useful. I'd recommend installing Wireshark or using "tcpdump -s 0 -X" to get the full packets dumped out. – Nathan Howell Jun 30 '12 at 23:55
  • Thanks for the suggestion. I've edited the question to link to full tcpdump output (with -s 0 -X) for both a successful and an unsuccessful upload. – Brent Yorgey Jul 01 '12 at 16:41

1 Answers1

4
21:59:56.813021 IP 192.168.1.148.39571 > ..http: Flags [.]
22:00:01.922598 IP ..http > 192.168.1.148.39571: Flags [F.]

The connection is closed by the server after a 3-4 sec timeout as the client didn't send any data, to prevent slowloris and similar ddos attacks. (F is the FIN flag, to close one direction of the bidirectional connection).

The server does not wait for the client to close the connection (wait for eof/0 == recv(fd)) but uses the close() syscall; the kernel on the server will respond with [R]eset packets if it receives further data, as you can see at the end of your dump.

I guess the client first opens the http connection and then prepares the data which takes too long.

Stefan
  • 5,304
  • 2
  • 25
  • 44
  • Thanks, this is exactly what I needed to know! Turns out Haskell's laziness was biting me in a big way. The fix is nontrivial (requires patching haxr) but I've gotten it to work now. – Brent Yorgey Jul 02 '12 at 22:57