Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IO::Hexdump for binary protocol inspection #3043

Merged
merged 2 commits into from
Aug 2, 2016

Conversation

ysbaddaden
Copy link
Contributor

@ysbaddaden ysbaddaden commented Jul 25, 2016

  • Rework Slice#Hexdump to separate each byte, instead of each short, and add an initial hex index (more common representation of hexdumps).
  • Add IO::Hexdump wrapping IO that prints a hexdump when reading or writing bytes from any IO.

Example:

00000000  20 21 22 23 24 25 26 27  28 29 2a 2b 2c 2d 2e 2f   !"#$%&'()*+,-./
00000010  30 31 32 33 34 35 36 37  38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?
00000020  40 41 42 43 44 45 46 47  48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
00000030  50 51 52 53 54 55 56 57  58 59 5a 5b 5c 5d 5e 5f  PQRSTUVWXYZ[\\]^_
00000040  60 61 62 63 64 65 66 67  68 69 6a 6b 6c 6d 6e 6f  `abcdefghijklmno
00000050  70 71 72 73 74 75 76 77  78 79 7a 7b 7c 7d 7e 7f  pqrstuvwxyz{|}~.
00000060  80 81 82 83 84                                    .....

class Hexdump
include IO

def initialize(@io : IO, @output : IO = STDERR, @read = false, @write = false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't the default output be STDOUT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's OK to use STDERR. For example today I was coding something and did ruby app.rb > data.json and wanted to debug it at the same time. For debugging I used STDERR.puts ... so I would get the debug data in the terminal but I would still get the output data in data.json without mixing it with debug code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I defaulted to STDERR for this very reason.

@ysbaddaden
Copy link
Contributor Author

@asterite I get a weird error on CI:

in ./src/io/hexdump.cr:38: instantiating '(HTTP::ChunkedContent | HTTP::Server::Response::Output | HTTP::Server::ReverseResponseOutput | HTTP::UnknownLengthContent | HTTP::WebSocket::Protocol::StreamIO | IO::ARGF | IO::BufferedWrapper | IO::Delimited | IO::FileDescriptor | IO::Hexdump | IO::RaiseErrno | IO::Sized | Int32 | JSON::PrettyWriter | MemoryIO | OpenSSL::SSL::Socket | PartialReaderIO | PointerIO | SimpleMemoryIO | String::Builder | Zlib::Deflate | Zlib::Inflate | Nil)#tap()'
      @io.write(buf).tap do |written_bytes|
                     ^~~
in ./src/io/hexdump.cr:39: instantiating 'Slice(UInt8)#[](Int32, (HTTP::ChunkedContent | Int32))'
        @output.puts buf[0, written_bytes].hexdump if @write && written_bytes
                        ^
in ./src/slice.cr:175: no overload matches 'Int32#<=' with type (HTTP::ChunkedContent | Int32)

I modified my patch to output the type of written_bytes as returned by @io.write in IO::Hexdump then run bin/crystal spec spec/std/http/client* spec/std/io/hexdump_spec.cr and it prints the following union instead of (Int32 | Nil):

(HTTP::ChunkedContent | HTTP::Server::Response | HTTP::Server::Response::Output | HTTP::UnknownLengthContent | HTTP::WebSocket::Protocol::StreamIO | IO::ARGF | IO::Delimited | IO::FileDescriptor | IO::Hexdump | IO::Sized | Int32 | MemoryIO | OpenSSL::SSL::Socket | PointerIO | String::Builder | Zlib::Deflate | Zlib::Inflate | Nil)

@asterite
Copy link
Member

asterite commented Aug 2, 2016

@ysbaddaden Oh!! I just remembered: IO#write(slice) writes the whole slice, and the return value doesn't matter. So in write's case we can hexdump the whole slice. And in read's case there's no need for the cast.

@ysbaddaden
Copy link
Contributor Author

OK I'll change that.

There is still something odd. For example all HTTP::ChunkedContent#write does is raising an exception, hence it's a NoReturn, but the compiler considers HTTP::ChunkedContent to be a possible return type for IO#write.

Verified

This commit was signed with the committer’s verified signature.
headius Charles Oliver Nutter
- initial 32bits hex index
- separate each byte (instead of each short)
- extra separator every 8 bytes

Verified

This commit was signed with the committer’s verified signature.
headius Charles Oliver Nutter
Transparent IO that prints an hexadecimal dump of read and written
bytes. Mostly useful for inspecting and debugging binary protocols.
@ysbaddaden
Copy link
Contributor Author

done.

@asterite
Copy link
Member

asterite commented Aug 2, 2016

@ysbaddaden I'll investigate it. Maybe some other IO returns self or something like that.

@asterite
Copy link
Member

asterite commented Aug 2, 2016

@ysbaddaden Found it, it's this line which got the type from this line

I think eventually it would be nice if a parent type could set the type for child methods, so we could mark IO#write as returning Nil, and so all implementors would return Nil too.

I also have to check why the IO is expanded there to all the types...

@asterite
Copy link
Member

asterite commented Aug 2, 2016

@ysbaddaden Thank you!! :-)

@asterite asterite merged commit a80db64 into crystal-lang:master Aug 2, 2016
@asterite asterite added this to the 0.19.0 milestone Aug 2, 2016
@ysbaddaden ysbaddaden deleted the std-io-hexdump branch August 2, 2016 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants