3

I can request a URL web service 'WS' directly from the browser, but when I use file_get_contents() or fopen methods in my code I get an error message. Does someone have a solution without using curl?

public function sendHttpRequest($data) {
   ini_set('user_agent', 'PHP');

    $context_options = array(
        'http' => array(
            'method' => 'POST',
            'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
            'content' => json_encode($data)
        )
    );


    $context = stream_context_create($context_options);

    //line error "line 196"
    $result = file_get_contents($this->WS, false, $context);
}

Error message:

failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request in /mnt/hgfs/case/src/bat/abc/send-entry.php on line 196

NULL

i've changed the context options to this, but still getting the HTTP/1.1 400 Bad Request

$context_options = array(
        'http' => array(
            'method' => 'POST',
            'header' => "Date: " . gmdate("D, d M Y H:i:s T", time()) . "\r\n"
            . get_headers($this->WS)[2] . "\r\n"
            . " x-powered-by: PHP/" . phpversion() . "\r\n"
            . " " . get_headers($this->WS)[5] . "\r\n"
            . " Pragma: no-cache" . "\r\n"
            . " Content-Length: " . strlen($this->content) . "\r\n"
            . " Content-Type: text/xml" . "\r\n"
            . " " .get_headers($this->WS)[9] . "\r\n"
            . " Connection: close",
            'content' => $this->content
        )

var_dump(get_headers($this->WS))

the data is sent to a server with adress http://192.168.xxx.xxx/WS

here is a var_dump of the array to send

array(16) {
  ["method"]=>
  string(24) "WS"
  ["LOGIN"]=>
  string(12) "xxx"
  ["DATE1"]=>
  string(10) "1970-01-01"
  ["DATE2"]=>
  string(0) ""
  ["TRANSMISSION"]=>
  INT(8)
  ["REF"]=>
  int(206)
  ["FORMAT"]=>
  int(7)
  ["DOMAIN"]=>
  int(3)
  ["TYPE1"]=>
  int(15)
  ["TYPE2"]=>
  NULL
  ["NAME"]=>
  string(12) "JDOE"
  ["ADRESSE"]=>
  string(0) ""
  ["ADRESSE2"]=>
  string(0) ""
  ["ADRESSE3"]=>
  string(0) ""
  ["ADRESSE4"]=>
  string(0) ""
  ["REF2"]=>
  string(0) ""
}

here is the full message error

Warning: file_get_contents(http://192.168.xxx.xxx/WS): failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request in /mnt/hgfs/case/src/bat/abc/send-entry.php on line 182

the working code:

    $context_options1 = array(
        'http' => array(
            'method' => 'POST',
            'header' => "Date: " . gmdate("D, d M Y H:i:s T", time())
            . "Accept: application/xml"
            . "Content-Type: application/xml"
            . "Content-Length: " . strlen($this->content),
            'content' => $this->content
        )
    );
Prosp
  • 151
  • 5
  • 17
  • If you are doing the same here as you do in the browser, then there is a whole lot of Javascript in the page calling the service which you've not told us about. There is no "directly from the browser" – symcbean Apr 28 '16 at 13:47
  • i mean to specify that the request to web service is working when i type the url in the browser but not when i call the function in the code, so the issue is in my function not in the web service – Prosp Apr 28 '16 at 14:00
  • http://php.net/manual/ru/function.stream-context-create.php#74795 – Valeriy Solovyov Apr 28 '16 at 14:02
  • Then maybe you should start by trying to do the same thing (i.e. GET not POST) in your PHP code. – symcbean Apr 28 '16 at 14:21
  • thanks for the advice. it's working with GET Method. so what's wrong with POST? – Prosp Apr 28 '16 at 14:31
  • Seems like, you build website in virtual box. If you added all headers, and still get errors. Try to use CURL. If cUrl worked succesfully, migrate options to Stream sockets. – Medet Ahmetson Atabayev Apr 29 '16 at 12:51

3 Answers3

2

A POST request needs a Content-Length header:

$content = json_encode($data);
$context_options = array(
        'http' => array(
            'method' => 'POST',
            'header' => "Content-Type: application/x-www-form-urlencoded\r\n" 
                         . "Content-Length: " . strlen($content) . "\r\n",
            'content' => $content
        )
    );

This is apparently done by the wrapper automagically, so there's something else. Perhaps omit the last linebreak ("\r\n") in headers.

Piskvor left the building
  • 91,498
  • 46
  • 177
  • 222
  • Does that work correctly with multibyte strings, and won't the stream context handle setting the content-length header? – CodeCaster Apr 28 '16 at 13:43
  • "14.13 Content-Length The Content-Length entity-header field indicates the size of the entity-body, in decimal number of OCTETs, sent to the recipient." So, yes: you're indicating the number of *bytes*, HTTP doesn't care about characters. As for the wrapper: it might (I know cURL does by default), or maybe not. – Piskvor left the building Apr 28 '16 at 13:45
  • I'm asking you whether the stream_context_create will set the content-length header by itself (the [documentation seems to hint towards that by omitting the header as well](http://www.php.net/manual/en/context.http.php)), in which case this suggestion won't fix the OP's problem. Disregard the `strlen()` part, that returns the number of bytes, not characters, so that'll work. – CodeCaster Apr 28 '16 at 13:46
  • Obviously you do, as you're asking me whether Content-Length wants the number of characters (nope, bytes, which is what what strlen counts: you'll get 2 for a single character encoded in 2 bytes - is that better than quoting the scripture directly?). As for the stream wrapper, the docs are unclear; perhaps a check of outgoing data is in order (one of the reasons I prefer the cURL extension). – Piskvor left the building Apr 28 '16 at 13:49
  • I am not asking you whether content-length wants characters, I was asking whether `strlen()` returns the appropriate number for use in a content-length header (namely bytes, not characters) in the case of strings containing multibyte characters. And it does, so that's that. My point: you're suggesting that the fix is to add a header that the library already adds by itself. QED below. – CodeCaster Apr 28 '16 at 13:51
  • @CodeCaster thanks for your reply, i added the content length but i still get the same error message. – Prosp Apr 28 '16 at 13:52
  • 1
    @CodeCaster: Ah, okay. I was wrong about the missing header then, plus I have misread your intent. I apologize for both. – Piskvor left the building Apr 28 '16 at 13:53
  • @Piskvor the omit of the line breaks doesn't make a change. but it's working when i change to GET method `'method' => 'GET',` then i put the parameters directly in the "WS" url. May be the issue is coming from POST method, or something missing in the context options!! Thanks for your time – Prosp Apr 28 '16 at 14:35
  • Thanks a lot. it's working without the linebreak. :) – Prosp May 02 '16 at 15:13
1

Emulate your browser first. Check whith Developer tools which headers it sends exactly to Service. And emulate it. It will help to find where you get wrong.

For example, Service may need "Content-type: multipart/form-data" instead of Application/x-form type. Maybe required Content-encoding or something else to be provided.

  • Thanks for your reply. i've checked with web developper. here are the headers that are sent: `Date: Thu, 28 Apr 2016 15:06:20 GMT` `Server: Apache/2.2.15 (CentOS)` `x-powered-by: PHP/5.4.5` `Expires: Thu, 19 Nov 1981 08:52:00 GMT` `Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0` `Pragma: no-cache` `Content-Length: 794``Content-Type: text/xml` `Connection:close` do i have to include them in the code? – Prosp Apr 28 '16 at 15:42
  • Yes. Try to connect with this requests. Does it work? – Medet Ahmetson Atabayev Apr 28 '16 at 15:51
  • i added the headers, but still i get the `"HTTP/1.1 400 Bad Request"`with post method. i've udated the post with how i did it. Thanks – Prosp Apr 29 '16 at 08:57
  • Can you give the web address, and POST parameters that you want to send? – Medet Ahmetson Atabayev Apr 29 '16 at 10:56
  • i've added the address and parameters. Thanks for your time – Prosp Apr 29 '16 at 12:00
0

I have a suspicion this is due to a mismatch between the announced mediatype and the actual payload. The proper status code to that would be 415: Unprocessable Entity, but 400 may appear as well.

To take your first example: You are announcing urlencoded data from a html form followed by JSON. I think the correct code here were this:

public function sendHttpRequest($data) {
    $data = json_encode($data)

    $context = stream_context_create(array(
        'http' => array(
            'method' => 'POST',
            'header' => array(
                'Content-Type: application/json',
                'Content-Length: ' . strlen($data),
                'Connection: close',
            ),
            'content' => $data,
        ),
    ));

    $result = file_get_contents($this->WS, false, $context);
}

You also seem to put a lot of headers into your request that simply do not belong there such as Pragma and other cache-control headers. Those should really be sent by the server, not the client.

Also, per this answer make sure whatever $this->WS contains is encoded soundly.

Community
  • 1
  • 1
DaSourcerer
  • 6,288
  • 5
  • 32
  • 55