I got a simpel question. Does anybody know how I can retrieve the cookies from a webservice in Delphi?
I've already tried the following code and I can read all data from the soap header but the cookies are missing and I know they are there because when I call a function from the webservice by SoapUI I can see them.
I made a simpel form an put a button on it. On click of the button event calls a function with the following instructions in it:
Any idea?
var
hInet: HINTERNET;
hConnect: HINTERNET;
infoBuffer: array [0..INTERNET_MAX_PATH_LENGTH] of char;
dummy: DWORD;
bufLen: DWORD;
okay: LongBool;
reply: String;
begin
hInet := InternetOpen(PChar(application.title), INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY, nil, nil, 0);
hConnect := InternetOpenUrl(hInet, PChar(url), nil, 0, INTERNET_FLAG_NO_UI, 0);
if not Assigned(hConnect) then
result := false
else
begin
dummy := 0;
bufLen := Length(infoBuffer);
okey := HttpQueryInfo(hConnect, HTTP_QUERY_RAW_HEADERS_CRLF, #infoBuffer[0], bufLen, dummy);
...
I also have a HTTPRIO component on my form which I use to communicate with the webservice. What I couldn't figure out is how I can use this component to get it work with the code above.
Thank you all in advance for your answer,
Farshid
When calling HttpQueryInfo(), the buffer size needs to be expressed in bytes, but you are expressing it in characters instead. If you are using Delphi 2009 or later (you did not say which you are using), then SizeOf(Char) is 2, not 1.
Try this:
bufLen := Length(infoBuffer) * SizeOf(Char);
okey := HttpQueryInfo(hConnect, HTTP_QUERY_RAW_HEADERS_CRLF, #infoBuffer[0], bufLen, dummy);
...
Or:
bufLen := SizeOf(infoBuffer);
okey := HttpQueryInfo(hConnect, HTTP_QUERY_RAW_HEADERS_CRLF, #infoBuffer[0], bufLen, dummy);
...
That being said, you can alternatively ask HttpQueryInfo() for the necessary byte size instead:
var
infoBuffer: array of Byte;
...
bufLen := 0;
okey := HttpQueryInfo(hConnect, HTTP_QUERY_RAW_HEADERS_CRLF, nil, bufLen, dummy);
if (not okey) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
begin
SetLength(infoBuffer, bufLen);
okey := HttpQueryInfo(hConnect, HTTP_QUERY_RAW_HEADERS_CRLF, Pointer(infoBuffer), bufLen, dummy);
end;
if okey then
begin
// use PChar(infoBuffer) up to (Length(infoBuffer) div SizeOf(Char)) characters...
...
end;
Related
I am covering project with tests and for that purpose I need dummy TCP Server, which could accept connection, write/read data to/from it, close it etc... I have found this question on stack overflow, covering mocking connection, but it doesn't cover what I actually need to test.
My idea relies on this article as starting point, but when I started implementing channel to let server write some data to newly opened connection, I got stuck with undebuggable deadlock in writing to channel.
What I want to achieve is to write some data to server's channel, say sendingQueue chan *[]byte, so later corresponding []byte will be sent to newly established connection.
During these little research I have tried debugging and printing out messages before/after sending data to channel and trying to send / read data from channel in different places of program.
What I found out:
My idea works if I add data directly in handleConnection with
go func() {
f := []byte("test.")
t.sendingQueue <- &f
}()
My idea doesn't work if I push data to channel from TestUtils_TestingTCPServer_WritesRequest in any form, either with func (t *TCPServer) Put(data *[]byte) (err error) or directly with:
go func(queue chan *[]byte, data *[]byte) {
queue <- data
}(t.sendingQueue, &payload)
It doesn't matter if channel is buffered or not.
So, obviously, there is something wrong either with the way I debug my code (I didn't dive into cli dlv, using just IDE debugger), or something that I completely miss about working with go channels, goroutines or net.Conn module.
For convenience public gist with full code is available. Note — there is // INIT part in the TestUtils_TestingTCPServer_WritesRequest which is required to run/debug single test. It should be commented out when running go test in the directory.
utils.go:
// NewServer creates a new Server using given protocol
// and addr.
func NewTestingTCPServer(protocol, addr string) (*TCPServer, error) {
switch strings.ToLower(protocol) {
case "tcp":
return &TCPServer{
addr: addr,
sendingQueue: make(chan *[]byte, 10),
}, nil
case "udp":
}
return nil, errors.New("invalid protocol given")
}
// TCPServer holds the structure of our TCP
// implementation.
type TCPServer struct {
addr string
server net.Listener
sendingQueue chan *[]byte
}
func (t *TCPServer) Run() (err error) {}
func (t *TCPServer) Close() (err error) {}
func (t *TCPServer) Put(data *[]byte) (err error) {}
func (t *TCPServer) handleConnection(conn net.Conn){
// <...>
// Putting data here successfully sends it via freshly established
// Connection:
// go func() {
// f := []byte("test.")
// t.sendingQueue <- &f
// }()
for {
fmt.Printf("Started for loop\n")
select {
case data := <-readerChannel:
fmt.Printf("Read written data\n")
writeBuffer.Write(*data)
writeBuffer.Flush()
case data := <-t.sendingQueue:
fmt.Printf("Read pushed data\n")
writeBuffer.Write(*data)
writeBuffer.Flush()
case <-ticker:
fmt.Printf("Tick\n")
return
}
fmt.Printf("Finished for loop\n")
}
}
utils_test.go
func TestUtils_TestingTCPServer_WritesRequest(t *testing.T) {
payload := []byte("hello world\n")
// <...> In gist here is placed INIT piece, which
// is required to debug single test
fmt.Printf("Putting payload into queue\n")
// This doesn't affect channel
err = utilTestingSrv.Put(&payload)
assert.Nil(t, err)
// This doesn't work either
//go func(queue chan *[]byte, data *[]byte) {
// queue <- data
//}(utilTestingSrv.sendingQueue, &payload)
conn, err := net.Dial("tcp", ":41123")
if !assert.Nil(t, err) {
t.Error("could not connect to server: ", err)
}
defer conn.Close()
out := make([]byte, 1024)
if _, err := conn.Read(out); assert.Nil(t, err) {
// Need to remove trailing byte 0xa from bytes array to make sure bytes array are equal.
if out[len(payload)] == 0xa {
out[len(payload)] = 0x0
}
assert.Equal(t, payload, bytes.Trim(out, "\x00"))
} else {
t.Error("could not read from connection")
}
}
After a help from a colleague and reading the article on how init works, I found a problem.
It was in init function, which was recreating extra server, due to using := assignment. I also updated code to make sure server runs before net.Dial and conn.Read.
I want to use Delphi code , export via DLL from C++ Builder
Delphi Code fragment goes like this
// function declare
function NameData(ItemIndex: Integer;
Buffer: PAnsiChar; var BufSize: DWORD): DWORD; stdcall;
external 'database.dll'
// function calling code
s1, S2: AnsiString;
begin
for i := 1 to ... do
begin
BufSize := 0;
NameData(i, nil, BufSize);
SetLength(s1, BufSize);
NameData(i, PAnsiChar(s1), BufSize);
mmo_dll.lines.Add(' name -> ' + string(s1));
relevant DLL code
library DLLCode;
function NameData(ItemIndex: Integer;
Buffer: PAnsiChar; var BufSize: DWORD): DWORD; stdcall;
var
returnString: Ansistring;
begin
returnString := ' call some other functions .....';
if BufSize < Length(returnString) then
result := ERROR_BUFFER_TOO_SMALL
else
begin
StrPCopy(Buffer, returnString);
result := ERROR_NO_ERROR;
end;
BufSize := Length(returnString);
end;
this and a lot of more stuff works fine, Delphi and Delphi DLL.
Now here is my not working C++ code :
// function prototype
typedef void (__stdcall*IntCharIntIn_VoidOut)(int, PAnsiChar, int);
// DLL prototype
extern "C" __declspec(dllimport)
IntCharIntIn_VoidOut __stdcall NameData(int, PAnsiChar, int);
// instance declaration
IntCharIntIn_VoidOut NameData;
// load library data, no error raise, other simpler function call already working
........
NameData = (IntCharIntIn_VoidOut)::GetProcAddress(load,
"NameData");
/// calling code
int Bufsize;
PAnsiChar DataName;
for (i = 0; i < count - 1; i++) {
*Bufsize = 0;
NameData(i, NULL, Bufsize);
StrLen(SignalName);
NameData(i, DataName, Bufsize );
Memo1->Lines->Add(IntToStr(i)); // for test only
}
In the second call I get an access violation, but can't see why/where I'm wrong
You don't allocate any memory, and your function declaration is wrong.
The function really should be declared like so:
typedef void (__stdcall *IntCharIntIn_VoidOut)(int, char*, unsigned int*);
And your calling code should be:
unsigned int Bufsize;
char* DataName;
for (i = 0; i < count - 1; i++) {
Bufsize = 0;
NameData(i, NULL, &Bufsize);
DataName = new char[Bufsize + 1];
NameData(i, DataName, &Bufsize);
// do something with DataName
delete[] DataName;
}
I've omitted error checking on the memory allocation and deallocation. If it were me I would be using grown up C++ string objects and not raw memory. The loop looks like it misses the final iteration, should be <= count - 1 or < count surely. Your type name, IntCharIntIn_VoidOut fails to recognise that two of the arguments are pointers. I'm using char* rather than PAnsiChar, but I guess that the latter is just an alias to the former.
I'll leave all of the above for you to deal with.
I'm creating a micro service to handle some attachments uploads to Amazon S3, What I'm trying to achieve is accept a file and then store it directly to my Amazon S3 bucket, my current function :
func upload_handler(w http.ResponseWriter, r *http.Request) {
file, header, err := r.FormFile("attachment")
if err != nil {
fmt.Fprintln(w, err)
return
}
defer file.Close()
fileSize, err := file.Seek(0, 2) //2 = from end
if err != nil {
panic(err)
}
fmt.Println("File size : ", fileSize)
bytes := make([]byte, fileSize)
// read into buffer
buffer := bufio.NewReader(file)
_, err = buffer.Read(bytes)
auth := aws.Auth{
AccessKey: "XXXXXXXXXXX",
SecretKey: "SECRET_KEY_HERE",
}
client := s3.New(auth, aws.EUWest)
bucket := client.Bucket("attachments")
err = bucket.Put(header.Filename, bytes, header.Header.Get("Content-Type"), s3.ACL("public-read"))
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
The problem is that the files stored in S3 are all corrupted, After a small verification it seems that the file payload is not read as bytes
How to convert the file to bytes and store it correctly to S3 ?
Use ioutil.ReadAll:
bs, err := ioutil.ReadAll(file)
// ...
err = bucket.Put(
header.Filename,
bs,
header.Header.Get("Content-Type"),
s3.ACL("public-read"),
)
Read is a lower-level function which has subtle behavior:
Read reads data into p. It returns the number of bytes read into p. It calls Read at most once on the underlying Reader, hence n may be less than len(p). At EOF, the count will be zero and err will be io.EOF.
So what was probably happening was some subset of the file data was being written to S3 along with a bunch of 0s.
ioutil.ReadAll works by calling Read over and over again filling a dynamically expanding buffer until it reaches the end of the file. (so there's no need for the bufio.Reader either)
Also the Put function will have issues with large files (using ReadAll means the entire file must fit in memory) so you may want to use PutReader instead:
bucket.PutReader(
header.Filename,
file,
fileSize,
header.Header.Get("Content-Type"),
s3.ACL("public-read"),
)
Help, I'm getting crazy :)
I'm playing around with TeamSpeak3 and have a dll call which returns an array of Clients (ID's) online. My problem is that it is returned in a structure I cannot figure out to read in Object Pascal.
The SDK manual specifies this:
This is what the docs says
To get a list of all currently visible clients on the specified virtual server:
unsigned intts3client_getClientList(serverConnectionHandlerID, result);
uint64 serverConnectionHandlerID;
anyID** result;
Parameters
• serverConnectionHandlerID
ID of the server connection handler for which the list of clients is requested.
• result
Address of a variable that receives a NULL-termianted array of client IDs. Unless an error occurs, the array must be released
using ts3client_freeMemory.
Returns ERROR_ok on success, otherwise an error code as defined in public_errors.h. If an error has occured, the
result array is uninitialized and must not be released.
This is my Delphi code so far.
type
PPanyID = ^PAnyID;
PanyID = ^anyID;
anyID = word;
// declaration looks like this, some guy called CodeJunkie ported it from C++)
function ts3client_getClientList(serverConnectionHandlerID: uint64; result: PPanyID): longword; cdecl; external CLIENT_DLL {$IFDEF MACOS}name '_ts3client_getClientList'{$ENDIF};
procedure TfrmMain.RequestOnlineClients;
var
clientids : PAnyId;
i : Integer;
begin
error := ts3client_getClientList(FTSServerHandlerID, #clientids);
if (error <> ERROR_ok) then
begin
if (ts3client_getErrorMessage(error, #errormsg) = ERROR_ok) then
begin
LogMsg(Format('Error requesting online clients: %s', [errormsg]));
ts3client_freeMemory(errormsg);
end;
end else
begin
// Put in into some regular Object array here, but how the h......
// Or at least interate the received array of ID's
end;
end;
The example code in C++ looks like this and it works, I didn't write it :)
void showClients(uint64 serverConnectionHandlerID) {
anyID *ids;
anyID ownClientID;
int i;
unsigned int error;
printf("\nList of all visible clients on virtual server %llu:\n", (unsigned long long)serverConnectionHandlerID);
if((error = ts3client_getClientList(serverConnectionHandlerID, &ids)) != ERROR_ok) { /* Get array of client IDs */
printf("Error getting client list: %d\n", error);
return;
}
if(!ids[0]) {
printf("No clients\n\n");
ts3client_freeMemory(ids);
return;
}
/* Get own clientID as we need to call CLIENT_FLAG_TALKING with getClientSelfVariable for own client */
if((error = ts3client_getClientID(serverConnectionHandlerID, &ownClientID)) != ERROR_ok) {
printf("Error querying own client ID: %d\n", error);
return;
}
for(i=0; ids[i]; i++) {
char* name;
int talkStatus;
if((error = ts3client_getClientVariableAsString(serverConnectionHandlerID, ids[i], CLIENT_NICKNAME, &name)) != ERROR_ok) { /* Query client nickname... */
printf("Error querying client nickname: %d\n", error);
break;
}
if(ids[i] == ownClientID) { /* CLIENT_FLAG_TALKING must be queried with getClientSelfVariable for own client */
if((error = ts3client_getClientSelfVariableAsInt(serverConnectionHandlerID, CLIENT_FLAG_TALKING, &talkStatus)) != ERROR_ok) {
printf("Error querying own client talk status: %d\n", error);
break;
}
} else {
if((error = ts3client_getClientVariableAsInt(serverConnectionHandlerID, ids[i], CLIENT_FLAG_TALKING, &talkStatus)) != ERROR_ok) {
printf("Error querying client talk status: %d\n", error);
break;
}
}
printf("%u - %s (%stalking)\n", ids[i], name, (talkStatus == STATUS_TALKING ? "" : "not "));
ts3client_freeMemory(name);
}
printf("\n");
ts3client_freeMemory(ids); /* Release array */
}
Please, can anybody help out here??
The pointer you get back points to the first result. Doing Inc(clientids) moves you on to the next result. Keep going until you reach an anyid that is zero ("NULL").
(You also need to save the original value of clientids so you can pass it to the free function as the documentation says to avoid leaking the memory.)
G is this for real, is this seriously the only way of reading arrays returned from a c++ dll? And will this work with 64bit too?
procedure TfrmMain.RequestOnlineClients;
var
ids : array of anyID;
pids : PanyID;
aid : anyID;
begin
error := ts3client_getClientList(FTSServerHandlerID, #ids);
if (error <> ERROR_ok) then
begin
if (ts3client_getErrorMessage(error, #errormsg) = ERROR_ok) then
begin
LogMsg(Format('Error requesting online clients: %s', [errormsg]));
ts3client_freeMemory(errormsg);
end;
end else
begin
pids := #ids[0];
while (pids^ <> 0) do
begin
aid := pids^;
LogMsg(format('id %u',[aid]));
inc(pids);
end;
ts3client_freeMemory(#pids^);
end;
end;
I'm trying to catch all OutputDebugString messages (including those from services) using the following code. It worked fine until I migrated to Windows 7.
The problem is that since Windows Vista services are running in the low level Session #0, some people say that it's impossible to catch them and some that it is. What do you think?
Is it possible to modify the following code by increasing some rights to be able to receive OutputDebugString messages from the Session #0? In other words; is it possible to share DBWIN_BUFFER in the session #0 with Session #1?
I would say it should be possible because e.g. DebugView can do that, and I can't see any service helper which would send those messages (e.g. through the named pipes) from the Session #0 to Session #1, where the GUI's running.
The problem will be IMO in the security settings. Can anyone suggest me how to modify them?
type
TODSThread = class(TThread)
protected
procedure Execute; override;
end;
...
procedure TODSThread.Execute;
var SharedMem: Pointer;
SharedFile: THandle;
WaitingResult: DWORD;
SharedMessage: string;
DataReadyEvent: THandle;
BufferReadyEvent: THandle;
SecurityAttributes: SECURITY_ATTRIBUTES;
SecurityDescriptor: SECURITY_DESCRIPTOR;
begin
SecurityAttributes.nLength := SizeOf(SECURITY_ATTRIBUTES);
SecurityAttributes.bInheritHandle := True;
SecurityAttributes.lpSecurityDescriptor := #SecurityDescriptor;
if not InitializeSecurityDescriptor(#SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION) then
Exit;
if not SetSecurityDescriptorDacl(#SecurityDescriptor, True, nil, False) then
Exit;
BufferReadyEvent := CreateEvent(#SecurityAttributes, False, True, 'DBWIN_BUFFER_READY');
if BufferReadyEvent = 0 then
Exit;
DataReadyEvent := CreateEvent(#SecurityAttributes, False, False, 'DBWIN_DATA_READY');
if DataReadyEvent = 0 then
Exit;
SharedFile := CreateFileMapping(THandle(-1), #SecurityAttributes, PAGE_READWRITE, 0, 4096, 'DBWIN_BUFFER');
if SharedFile = 0 then
Exit;
SharedMem := MapViewOfFile(SharedFile, FILE_MAP_READ, 0, 0, 512);
if not Assigned(SharedMem) then
Exit;
while (not Terminated) and (not Application.Terminated) do
begin
SetEvent(BufferReadyEvent);
WaitingResult := WaitForSingleObject(DataReadyEvent, INFINITE);
case WaitingResult of
WAIT_TIMEOUT: Continue;
WAIT_OBJECT_0:
begin
SharedMessage := String(PAnsiChar(SharedMem) + SizeOf(DWORD));
// here I have what I need and process it in the main thread
end;
WAIT_FAILED: Continue;
end;
end;
UnmapViewOfFile(SharedMem);
CloseHandle(SharedFile);
end;
I've added the C# tag even if the code is in Delphi because the security attributes are common for the whole Windows API and C# has many followers :)
Someone talked about the same issue in the SysInternals forums. Their solution was to add "Global\" to the named objects.
So use the following
CreateEvent(#SecurityAttributes, False, True, 'Global\DBWIN_BUFFER_READY');
CreateEvent(#SecurityAttributes, False, False, 'Global\DBWIN_DATA_READY');
CreateFileMapping(THandle(-1), #SecurityAttributes, PAGE_READWRITE, 0, 4096, 'Global\DBWIN_BUFFER');